Skip to content

docs(guidelines): internal-doc writing style, size discipline, six harvest runs - #10030

Open
saltas888 wants to merge 17 commits into
developfrom
pha/conventions
Open

docs(guidelines): internal-doc writing style, size discipline, six harvest runs#10030
saltas888 wants to merge 17 commits into
developfrom
pha/conventions

Conversation

@saltas888

@saltas888 saltas888 commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Why

Reviewers kept raising the same things, and each lesson lived in a review thread and died there. This PR consolidates six /harvesting-review passes over PRs reviewed 2026-07-16 → 2026-08-07 into the internal-doc layer, so a future author (human or agent) follows the convention the first time. It also introduces the discipline the harvests themselves turned out to need: a harvest that only adds makes the layer worse, so every addition here is paid for with compression or pruning.

Every lesson was reconstructed against the before/after code, verified against the current codebase, and checked against existing coverage before landing, per .agents/skills/harvesting-review/.

Closed in favor of this PR (their commits are cherry-picked here): #10098, #10145, #10157.

Docs only — no code, no generated files, no schema.

The conventions now in place

One row per lesson: the source PR whose review surfaced it, and where it now lives.

Backend

Convention Lives in Source
Ask whether the try/except is needed at all; # noqa: BLE001 needs a named reason; inside a transaction a caught-and-returned failure commits (verified against InfrahubDatabase.__aexit__) dev/guidelines/backend/exceptions.md (new, split from python.md) #10002
Don't write T | Sequence[T] unions — take Sequence[T] and let callers wrap (a bare str satisfies Sequence[str] and gets shredded) dev/guidelines/backend/python.md #10079
tasks/*.py backend imports stay function-local — and the exception covers a thin wrapper, not logic hosted in tasks/ dev/guidelines/backend/python.md #9814, #10095
A path-exclusion check must not use a bare startswith dev/guidelines/backend/asgi-middleware.md (new, split from python.md) #9879
retry_db_transaction replays the whole wrapped scope (on TransientError or EntityNotFound): wrap only rollback-able scopes, skip under db.is_transaction, mind nested retried callers dev/knowledge/backend/mutations.md #10121
Pass limit/offset to the base Query; insert_limit = False without an own LIMIT never finishes paging; auto-pagination needs a total-order ORDER BY; read collect() columns with get_as_list_of_type dev/knowledge/backend/query-pattern.md #10106, #10132
n.kind is concrete-only — generics exist as labels; type concrete-only query inputs as list[NodeSchema] dev/knowledge/backend/database-schema.md #9805
Changelog models mask secrets only at construction — post-construction assignment leaks them dev/knowledge/backend/events.md #10105
InfrahubBatch is concurrent, not ordered; the best-effort contract also covers a single inline side effect; add_tags() mid-run drops earlier tags (cross-linked where the guide teaches the pattern) dev/knowledge/backend/async-tasks.md, dev/guides/backend/creating-async-tasks.md #10113, #10034
Fix data bugs in the migration layer, not lazily in save(); per-item try/collect into MigrationResult.errors; batch by the memory-bounding unit dev/guides/backend/creating-migrations.md (now routed from backend/AGENTS.md — it was loaded by nothing) #10105, #10132
A new Settings field must reach the hand-maintained development/ compose anchor, not just the generated one dev/guidelines/backend/checklist.md Configuration #10122
New REST endpoints join Ask First — extend the existing GraphQL query instead backend/AGENTS.md #8594

Backend testing

Convention Lives in Source
Verify wiring by parsing source (ast/inspect), never by instrumenting production code dev/guidelines/backend/testing.md, .agents/rules/testing-python.md #10121
Don't add a return value just so a test can assert on it; assert an intermediate signal when a result converges via two code paths dev/guidelines/backend/testing.md #10031, #10038
Poll, don't sleep, for async outcomes; make removal assertions branch-attributable; extract a pure function before claiming the cheap test tier is unreachable dev/guidelines/backend/testing.md #10133, #10132, #10137
Parametrize with dataclasses — the dict-keys-plus-in-test-lookup hybrid is now named and forbidden dev/guidelines/backend/testing.md #10095
A benchmark must exercise the exact input shape a conditional optimization gates on dev/knowledge/backend/testing.md #10062

Frontend

Convention Lives in Source
Reset shared Jotai atoms, mocks (vi.resetAllMocks), and URL state between tests in one file dev/guides/frontend/writing-component-tests.md #10014
A test must fail when the behavior breaks: assert the value, not the static text beside it; check the component actually reads the flag the setup varies dev/guides/frontend/writing-component-tests.md #10111, #10128, #10129
A lazily-memoized module-level singleton needs a test reset hook dev/guides/frontend/writing-unit-tests.md #10059
A Map lookup seeded from the array you index it with is still a type lie under ! dev/guidelines/frontend/typescript.md #10014
Don't mirror owned state (URL, forms, server) into a Jotai atom — generalized from the old forms-only rule dev/guidelines/frontend/page-architecture.md #10111

Component design and code style (.agents/rules/ — edits to existing rules, no new files)

Convention Lives in Source
Build the component graph at the entry point in one pass; a wiring mismatch raises at construction, not as a runtime fallback; initialize()/reset() clears every derived cache .agents/rules/backend-component-design.md #10055, #10053/#10019/#10082
Spec vocabulary is as unreadable as a spec ID in a test name or docstring .agents/rules/code-doc-style.md #10096

Process and documentation

Convention Lives in Source
PR ceremony scales with the change: no changelog fragment or dev/specs/ scaffolding on a trivial fix; a lint-suppression PR ships the suppression only and files the bug separately dev/guidelines/git-workflow.md, dev/guidelines/repository-organization.md #10018, #10079
Internal-doc writing style: rule first, plain words, no provenance, no padding, a size budget, pay-for-it dev/guidelines/documentation.md For Internal Docs the harvests themselves
No file.py:line citations in internal docs; measurements go in as relative comparisons dev/guidelines/documentation.md #10038, #10108
Verify identifier/metric/config-default claims against the diff before writing them into fragments, PR text, or ADRs root AGENTS.md #9845 et al.
Changelog fragments: GitHub issue number or +, never a tracker ID; the + orphan-prefix failure mode; housekeeping is not a catch-all — no user-facing effect means no fragment .agents/skills/creating-changelog-entries/SKILL.md, docs/docs/development/changelog.mdx #9845, #10146/#10147, #10018
invoke lint runs ruff check --diff, blind to unfixable violations — /pre-ci carries the CI-parity ruff check step .agents/commands/pre-ci.md, root AGENTS.md #10146/#10147
Don't escalate from a stale "commits behind" count .agents/skills/monitoring-pull-requests/SKILL.md #10146
Grafana dashboards: defined datasource variables only, sweep every link surface, regenerate the standalone compose from development/ development/grafana/AGENTS.md (new) #10153
Excalidraw exports: opaque white background docs/AGENTS.md #9953

Pruned in the same PR

Stale PR #NNNN/Migrated in #NNNN citations across 5 frontend docs (17 hits) and templates.md (4 hits, plus the renamed method the rot was hiding: _apply_template()handle_object_template()); typescript.md's useMemo dependency-array teaching, which contradicted react.md's React-Compiler guidance — a reviewer had to ask which was right; schema-definitions.md's pointer to an issue closed 2026-07-20; query-pattern.md's five near-identical walkthroughs collapsed into one example plus an accessor table (520 → 459).

The harvesting-review skill, final state (0.8.0)

Refine, don't accrete leads the skill and outranks its other rules: measure the target file against its size range first, compress or split a file at its limit (repointing every inbound reference, including .agents/skills/.agents/commands routes), cut what the new rule supersedes, and report added/removed line counts — zero deletions is a finding about the harvest. Every run sweeps the destination layer (including .agents/rules) for citation rot, fixed defect-snapshots, and superseded narrow rules, and fixes every hit in the same PR — a punch list is not pruning. A lesson whose root cause is a fragile pattern becomes a rule steering away from the pattern, not a survival guide for it. A lesson owned by an existing skill (creating-changelog-entries, pre-ci, the org plugin's pruning-residues) is routed into that skill instead of parallel dev/ prose.

Also decided along the way

Context budget

+702/−338 over develop (40 files, after merging develop back in), and the shape matters more than the total: no new file in .agents/rules/ (the only layer injected into every agent turn — three existing rules edited), the two python.md splits load only on their trigger, and every harvest paid with deletions.

File Lines Range
dev/knowledge/backend/query-pattern.md 490 200–400 — the ## Internals split is the flagged next change
dev/guidelines/backend/python.md 414 100–400 — the ASGI split had it at 393; develop-side additions re-grew it; the next split is its own change
dev/guides/frontend/writing-component-tests.md 398 200–400
dev/guidelines/backend/testing.md 393 100–400

How to test

Docs only. markdown-lint (docs/docs), validate-dev-guideline-links (lychee over dev/**, backend/**), and validate-documentation-style (Vale over docs/) cover these paths in CI; relative links, section anchors, and code-fence balance checked locally across all 37 changed markdown files.

🤖 Generated with Claude Code

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

1 issue found across 3 files

Confidence score: 5/5

  • In dev/guidelines/changelog.md, the main risk is process drift: targeting develop for a docs-only change can bypass the base-branch policy and lead to guidance landing out of the expected release flow—retarget this PR to stable to keep documentation updates aligned with branch rules.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="dev/guidelines/changelog.md">

<violation number="1" location="dev/guidelines/changelog.md:55">
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.</violation>
</file>

Shadow auto-approve: would not auto-approve because issues were found.

Re-trigger cubic

Comment thread dev/guidelines/changelog.md Outdated

## 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>

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.

I do agree

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I know but there's a big deviation on the patterns that are described in develop and stable, therefore in stable we will have to describe code that actually doesn't exist. I'd need to check if we can split some parts

Comment thread dev/guidelines/repository-organization.md Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

0 issues found across 1 file (changes from recent commits).

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Shadow auto-approve: would not auto-approve. Auto-approval blocked by 2 unresolved issues from previous reviews.

Re-trigger cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

0 issues found across 3 files (changes from recent commits).

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Shadow auto-approve: would not auto-approve. Auto-approval blocked by 2 unresolved issues from previous reviews.

Re-trigger cubic

@saltas888
saltas888 force-pushed the pha/conventions branch 2 times, most recently from b14e38b to 001fd00 Compare August 6, 2026 10:45
@saltas888 saltas888 changed the title docs(guidelines): scope PR ceremony to change size [INBOX-20] docs(guidelines): internal-doc writing style, size discipline, three harvests Aug 6, 2026
@github-actions github-actions Bot added the group/backend Issue related to the backend (API Server, Git Agent) label Aug 6, 2026
@saltas888
saltas888 marked this pull request as ready for review August 6, 2026 10:56
@saltas888
saltas888 requested a review from a team as a code owner August 6, 2026 10:56
@codspeed-hq

codspeed-hq Bot commented Aug 6, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 13 untouched benchmarks


Comparing pha/conventions (754a267) with develop (d3817f1)

Open in CodSpeed

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

1 issue found across 7 files (changes from recent commits).

Confidence score: 5/5

  • In dev/guidelines/backend/python.md, extract/opsmill still points “New error handling conventions” to the old location even though guidance moved to dev/guidelines/backend/exceptions.md, which can send agents to stale instructions and cause inconsistent exception-handling patterns — update the routing/reference mapping to the new doc path.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="dev/guidelines/backend/python.md">

<violation number="1" location="dev/guidelines/backend/python.md:403">
P3: Now that Exception Handling lives in the new `dev/guidelines/backend/exceptions.md`, the extract/opsmill tooling still routes agents to `dev/guidelines/backend/python.md` for "New error handling conventions" — both in `.agents/commands/speckit.opsmill.extract.md` (lines ~106-107 and the template table) and `.agents/skills/speckit-opsmill-extract/SKILL.md` (lines ~108-109). Following those instructions would send a future contributor to a file that no longer carries the exception-handling content. The git-workflow.md change here shows the intent to repoint references; consider updating these .agents routing entries to `dev/guidelines/backend/exceptions.md` to keep the move consistent.</violation>
</file>

Shadow auto-approve: would not auto-approve because issues were found.
Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread backend/AGENTS.md Outdated
@@ -267,6 +267,22 @@ async def set(self, key: str, value: str, expires: KVTTL | int | None = None) ->

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: Now that Exception Handling lives in the new dev/guidelines/backend/exceptions.md, the extract/opsmill tooling still routes agents to dev/guidelines/backend/python.md for "New error handling conventions" — both in .agents/commands/speckit.opsmill.extract.md (lines ~106-107 and the template table) and .agents/skills/speckit-opsmill-extract/SKILL.md (lines ~108-109). Following those instructions would send a future contributor to a file that no longer carries the exception-handling content. The git-workflow.md change here shows the intent to repoint references; consider updating these .agents routing entries to dev/guidelines/backend/exceptions.md to keep the move consistent.

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

<comment>Now that Exception Handling lives in the new `dev/guidelines/backend/exceptions.md`, the extract/opsmill tooling still routes agents to `dev/guidelines/backend/python.md` for "New error handling conventions" — both in `.agents/commands/speckit.opsmill.extract.md` (lines ~106-107 and the template table) and `.agents/skills/speckit-opsmill-extract/SKILL.md` (lines ~108-109). Following those instructions would send a future contributor to a file that no longer carries the exception-handling content. The git-workflow.md change here shows the intent to repoint references; consider updating these .agents routing entries to `dev/guidelines/backend/exceptions.md` to keep the move consistent.</comment>

<file context>
@@ -476,5 +400,6 @@ For additional information around testing patterns refer to [./testing.md](./tes
 
 ## See Also
 
+- [Exception Handling](exceptions.md) - Catching, scoping, and suppressing exceptions
 - [Backend Architecture](../../knowledge/backend/architecture.md) - Backend architecture overview
 - [Git Workflow](../git-workflow.md) - Git workflow and commit conventions
</file context>

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.

Do these skills need to reference this exact file? It creates this kind of issue

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.

The skills need a concrete load target in the current routing design, but python.md is no longer the right target for exception handling. The parent comment still applies: these stale references should be repointed to dev/guidelines/backend/exceptions.md; this PR does not show a stable alternative routing mechanism.

saltas888 and others added 6 commits August 6, 2026 16:09
PR #10018 (INBOX-20) got a reviewer ask for the whole PR to be just the
5-line repository.py annotation fix: no dev/specs/ spec-kit scaffolding,
no changelog fragment for a change with no user-facing effect. Neither
constraint was written down anywhere, so an agent following the normal
speckit pipeline had no way to know to trim them. Document both:

- changelog.md: skip the fragment when a change has no user-facing
  effect; housekeeping is not a catch-all for anything code-adjacent.
- repository-organization.md: the spec-kit design record should be
  proportional to the change it documents, not a fixed-cost byproduct
  of running the workflow.
- git-workflow.md: cross-reference both from the Pull Requests section
  so the rule is reachable from root AGENTS.md's Coding Standards link.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013MwudkACUQdCZTJ5aVBnr3
PR #10079 widened RelationshipManager.update()'s data parameter from an
invariant list to a covariant Sequence to drop a call-site type: ignore.
The reviewer's "definitely would have missed this one" was for the
carve-out that makes it safe: str satisfies Sequence, so isinstance(data,
str) must be checked before isinstance(data, Sequence), or a bare peer id
gets iterated character-by-character. Nothing in the guidelines named this
gotcha, so document it next to the neighboring isinstance-narrowing and
list/Sequence-variance sections.

The PR's other two review threads (skip-changelog for no-user-visible-
effect changes, and proportional dev/specs/ ceremony) restate lessons
already codified on this branch by c1b89c4 (INBOX-20) — that commit
just hasn't reached develop yet, so PR #10079's branch never saw it. No
doc gap, no edit needed there.

The approving review's PeerWithRelationshipMetadata read/write-split
suggestion is a real, confirmed observation (manager.py and node/create.py
populate it asymmetrically) but is scoped to that one class, not a
recurring authoring convention — discarded from this harvest as PR-local
tech debt rather than encoded as a rule.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WgpvpBsruPYD3fYpyfzL7R
…NBOX-19]

PR #10002 (INBOX-19) re-enabled ruff's BLE (blind-except) rule and resolved
78 violations: 8 narrowed to specific exception types, 70 kept `except
Exception` with a justification comment + `# noqa: BLE001`. Reviewer
@ogenstad's threads on that PR, plus an independent cubic-dev-ai finding,
surfaced three durable gaps beyond "narrow when you can" (already
documented in python.md's Exception Handling section):

- No existing guidance asked whether a catch is needed *before* choosing
  its scope. @ogenstad on api/auth.py: the guarded event-emission goes to
  an internal queue that can't block the response, so the defensive catch
  is likely unnecessary altogether - a background task after the response
  is the better shape when isolation is genuinely required. Same pattern
  flagged on oauth2.py and oidc.py.
- "Deliberate design choice" (the bar for `# noqa: BLE001`) was previously
  illustrated only by "top-level boundary" (task worker loop, request
  handler) - too narrow for the batch/collection and best-effort shapes
  this PR actually used 70 times over. Named all three explicitly with a
  worked example.
- cubic-dev-ai flagged that the justification comment on
  m066_consolidate_duplicate_number_pools.py's except ("failures become
  MigrationResult errors") overstates safety: the `return` happens inside
  `async with db.start_transaction()`, so `InfrahubDatabase.__aexit__`
  (backend/infrahub/database/__init__.py:298-307) sees no exception and
  *commits* the partial work instead of rolling it back. Verified directly
  against both files. The new subsection requires a justification comment
  to state the actual commit/rollback consequence when nested inside a
  transaction, with a before/after example built on the real bug shape.

Also documented (@ogenstad, twice, on validators/tasks.py and
infrahub_load_tester.py): a lint-suppression PR that surfaces a
pre-existing behavioral bug should ship the annotation only and file the
bug separately, not fix it inline. New git-workflow.md bullet under Pull
Requests, cross-linked from the noqa subsection above since the same
transaction/commit bug is the concrete example of exactly this pattern.

Extended dev/knowledge/backend/async-tasks.md's existing "post-commit
follow-up is best-effort" contract with one sentence: it was scoped to
per-item batch dispatch, but the same shape (confirm the call can fail,
then isolate via a background task rather than an inline try/except)
applies to a single inline side effect in a request handler too.

Not encoded as a lesson: the three specific bugs flagged in review
(m066 commit-not-rollback, tasks.py retry-defeating catch,
infrahub_load_tester.py's undefined `all_branches` on failure) are
PR-local defects, not conventions - the review thread itself already
treats them as follow-up candidates pending a human filing decision.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K6uYDCYNXbWUdkFEJLW3Zx
…accrete rule

Internal docs had no concision rule and no size discipline, so harvested
lessons arrived padded and always additive: over the last four months
dev/guidelines and .agents/rules took +222/-16 lines, and this branch's own
harvests pushed backend/python.md to 480 lines against its 100-400 range.
The routing rules ("edit before create", "strengthen before duplicate") stop
duplicate rules, not growth — nothing in the flow ever removed a line.

Two additions:

- dev/guidelines/documentation.md gains "Writing Style / For Internal Docs",
  covering dev/, the AGENTS.md files, and .agents/ — rule first, plain words,
  no provenance, no padding, a budget, and pay-for-it: cut what the new rule
  supersedes and check the file's size range before appending.
- The harvesting-review skill leads with "Refine, don't accrete", flagged as
  outranking its other rules: measure the file first, compress or split one
  that is at its limit, cut superseded prose, and report added/removed line
  counts, since a harvest that deletes nothing accreted rather than refined.

Paid for the skill's new section by compressing its 1A load-trigger block,
which had grown into eight lines telling the reader to keep entries short.

Also drops the routing example that pointed at dev/guidelines/changelog.md,
which no longer exists now that changelog conventions live in the
creating-changelog-entries skill, and applies the new brevity rule to the
sections this branch added that read long.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
python.md had reached 480 lines against the 100-400 range that
repository-organization.md sets for dev/guidelines, and this branch's
harvests added 55 of those. Applying the rule the previous commit
introduced: the file at its limit gets split, not extended.

Exception Handling and the # noqa: BLE001 subsection move verbatim to
dev/guidelines/backend/exceptions.md, which leaves python.md at 405 lines
and gives the exception rules their own loadable home. Dropped the
standalone "broad except is justified only at a top-level boundary"
paragraph on the way, since the noqa list's first bullet already says it.

Load-triggers updated so the new file is reachable: entries in
backend/AGENTS.md, root AGENTS.md, and dev/README.md, plus a See Also link
from python.md. The two inbound anchor links (git-workflow.md and
knowledge/backend/async-tasks.md) now point at the new file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
PR #10146 (INBOX-30) removed a stale mypy override with no source change,
and its review threads surfaced three durable gaps. Two were suggested
candidates going in; investigation confirmed one and sharpened the other,
plus turned up a third while mining the same threads.

- cubic-dev-ai flagged a changelog fragment missing towncrier's `+` orphan
  prefix. The rule already exists in four places (the skill file, two
  backend/AGENTS.md refs, git-workflow.md, and the PR template checklist),
  so this is "covered but still flagged," not missing: the doc states the
  rule but never the failure mode, which is the concrete detail cubic had
  to supply. Added the consequence (bogus issue link / skipped fragment)
  to creating-changelog-entries/SKILL.md's Common Mistakes list.

- Reviewer @ogenstad's "this needs to be rebased" corrected an escalation
  that reasoned from "0 commits behind, so a rebase is a no-op." That
  reasoning was actually correct at the moment it was made — the defect
  was trusting it unchanged after time passed (waiting on the review) and
  the upstream base got fixed in the interim (#10147). Neither
  rebase/SKILL.md (which documents a different mistake: counting against
  a stale local ref instead of origin/<base>) nor monitoring-pull-
  requests/SKILL.md addressed re-verifying a stale snapshot. Added a
  callout to the latter's Phase 2: re-fetch and recompute immediately
  before deciding, reproduce against the base's current tip, and treat a
  reviewer's rebase suggestion as a cue to re-verify rather than repeat an
  earlier count.

- Self-disclosed in the same thread: `invoke lint`/`invoke backend.lint`
  run `ruff check --diff` (tasks/main.py:39, tasks/backend.py:101), which
  reports only auto-fixable rewrites and exits 0 on an unfixable
  violation. CI's python-lint job runs a plain `ruff check`
  (.github/workflows/ci.yml:325-326), so a locally-green lint run is not
  proof CI's will be. Verified both invocations directly before writing
  this down. Documented next to the commands themselves in root
  AGENTS.md's Linting & Formatting section.

Not encoded: @ogenstad's unresolved "not sure we need a changelog entry
for something like this" — no reply landed and the fragment shipped
as-is, so there's no correction to derive a rule from. The existing
housekeeping rule already tells the author to ask when unsure; whether
this specific case should have triggered that is a human call, not
something this thread settles.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KWG4Cvk9ZzseV73X9o1QTj
Comment thread backend/AGENTS.md Outdated
@@ -267,6 +267,22 @@ async def set(self, key: str, value: str, expires: KVTTL | int | None = None) ->

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.

Do these skills need to reference this exact file? It creates this kind of issue

Comment thread dev/guidelines/backend/python.md Outdated
Comment on lines +299 to +313
### `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]
```

Widening a parameter from `list[T]` to `Sequence[T]` means widening this runtime check in step: an annotation that newly accepts `str` while the `isinstance` check still assumes a `list` misroutes every bare string. Test both the bare `str` and the newly-accepted non-`list` sequence (a `tuple`).

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.

This is a personal take but I am not a huge fan of this pattern. I think we should get rid of them instead of patching potential typing bugs with a dedicated section inside our documentations.
In my opinion, let's use a Sequence or a list directly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed, replaced the section with a rule against the pattern itself, take Sequence[T] and let callers wrap. Kept one line on the str carve-out for existing code.

- **Say it once**: if the rule is already written elsewhere, link to it instead of restating it
- **Budget**: a new rule is a few lines plus one example. If it needs more than that, it's an
explanation for `dev/knowledge/`, not a guideline
- **Pay for it**: cut the prose the new rule supersedes, and check the file against its size range

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.

Should we use the pruning-residues skill and iterate over it if there is any enhancement to do on it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The harvesting skill now routes lessons into the skill that owns the workflow and runs the diff through pruning-residues when available. It's not in our skills-lock yet, will add it as a follow-up.

Comment thread AGENTS.md
Comment on lines 93 to +106
### Linting & Formatting

```bash
uv run invoke format # Format all Python code
uv run invoke lint # Lint all Python code
cd frontend/app && pnpm biome:fix # Format/lint frontend
uv run invoke docs.lint # Lint documentation
```

`invoke lint`/`invoke backend.lint` run `ruff check --diff`, which reports only auto-fixable issues
and exits 0 on an unfixable violation (e.g. an unsuppressed `BLE001`) that CI's plain `ruff check`
still fails on. Run `uv run ruff check <path>` directly to see the full picture before trusting a
clean local run.

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.

Just a comment in case this sounds useful: a /pre-ci skill exists with a --fast option that trigger a bunch of CI checks locally. These checks include these lint steps

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

right, done

saltas888 and others added 4 commits August 12, 2026 17:40
Twelve merged PRs audited; the lessons that generalize are routed into the
internal-doc layer, and the citation rot earlier runs left behind is fixed in
the same pass.

New rules: a benchmark must exercise the input shape a conditional
optimization is gated on; reset shared Jotai atoms, mocks and URL state
between component tests in one file; tasks/*.py imports stay function-local;
a path-exclusion check must not use a bare startswith; a component's
initialize() clears every derived cache, not just the obvious field;
changelog fragment names use the GitHub issue number, never an internal
tracker ID; don't add a return value to a mutating method just so a test can
assert on it; a Map lookup seeded from the array you index it with is still a
type lie when asserted with !; no file.py:line citations in internal docs.

Strengthened where a rule existed but a reviewer still had to raise it: the
module-singleton reset-hook case in the frontend unit-test guide, and a
cross-link to the add_tags() pitfall where the async-task guide teaches the
pattern that reproduces it.

Pruned: stale PR/issue citations across five files, a defect-snapshot note
that described a current bug rather than a durable convention, and a
templates.md reference to a method that had since been renamed.

Replayed from stable onto the internal-doc-style branch so both harvests land
as one stack. Two reconciliations where the branches overlapped:

- The skill's rot sweep now sits under the "Refine, don't accrete" principle
  the base branch added rather than beside it, and the apply step applies both.
- The no-file.py:line rule joins the sibling work-item-citation rule in
  documentation.md's Don't list; the base branch's "No provenance" bullet
  points at both instead of restating them.

Splitting ASGI Middleware out of python.md pays for the path-matching lesson
this run adds. With it the file would have reached 431 lines against a
100-400 range, and the rule the base branch introduced says a file at its
limit gets split, not extended. python.md ends at 393.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit facc5d3)
Fifteen merged PRs audited across develop and release-1.11. Three of the
lessons were already written down and a reviewer still had to raise them, so
those are strengthened in place rather than duplicated.

Strengthened:

- backend-component-design: the whole object graph is built at the entry
  point in one pass, not part-way through a run, and invalid wiring raises
  while the graph is built instead of degrading to a runtime fallback. One
  reviewer made this point four times in two PRs against a rule that is
  auto-injected on every turn; it only forbade construction inside __init__.
- backend/testing: names the parametrize shape that actually got written —
  dict keys in the decorator, values looked up inside the test.
- frontend/page-architecture: the no-mirroring rule was stated for forms and
  useState only, so URL state copied into a Jotai atom slipped past it.

Added:

- query-pattern: pass limit/offset to the base Query constructor, since
  execute() reads self.limit/self.offset to choose how to run and pages the
  query in chunks when both are None; and read a collect() column with
  get_as_list_of_type rather than unpacking rows.
- code-doc-style: spec vocabulary is as unreadable as a spec ID in a test
  name or docstring.
- documentation: measurements go in as relative comparisons, not absolute
  figures that depend on the environment they were taken in.
- frontend/writing-component-tests: a test has to fail when the behavior
  breaks — assert the value and not the static text around it, and check the
  component reads the flag the setup varies.
- python: the tasks/*.py function-local import exception covers a thin
  wrapper, not logic hosted in tasks/.

Pruned:

- typescript.md taught useMemo/useCallback dependency arrays while
  knowledge/frontend/react.md says not to use them at all under the React
  Compiler. A reviewer had to ask which was right.
- schema-definitions.md pointed at an issue for planned improvements that
  closed on 2026-07-20.
- query-pattern.md was 520 lines against a 200-400 range and this run adds
  to it, so its five near-identical result-dataclass walkthroughs collapse
  into one example plus an accessor table. The file ends at 459.

Net -40 lines.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 4c4a19d)
A harvesting-review run over the week's review threads, restricted to the
PRs the in-flight harvests (#10145, #10098) did not read: open and
recently-merged PRs across develop, stable, and release-1.11.

New rules and knowledge:
- mutations.md: retry_db_transaction placement — wrap only rollback-able
  transaction scopes, skip under a caller-supplied transaction (#10121)
- query-pattern.md: READ queries with insert_limit=False need their own
  LIMIT; auto-paginated reads need a total-order ORDER BY (#10132)
- creating-migrations.md: fix data bugs at the migration layer, not in
  runtime save paths (#10105); per-item error collection (#10132);
  batch by the memory-bounding unit (#10132)
- database-schema.md: generic kinds exist only as labels — n.kind never
  matches a generic; type concrete-only inputs as list[NodeSchema] (#9805)
- events.md: changelog models mask secrets only at construction —
  post-hoc assignment leaks them into events (#10105)
- async-tasks.md: InfrahubBatch is concurrent, not ordered (#10113)
- testing.md + testing-python.md rules: wiring tests parse source with
  ast/inspect instead of instrumenting production code (#10121);
  poll-don't-sleep for async effects (#10133); branch-attributable
  removal assertions (#10132); pure-function extraction before skipping
  the cheap test tier (#10137)
- checklist.md: new Settings fields must reach both compose entry points
  — one generated and CI-checked, one hand-maintained (#10122)
- backend/AGENTS.md: new REST endpoints are ask-first — prefer existing
  GraphQL for SDK needs (#8594); route creating-migrations.md in Guides
- pre-ci.md: invoke lint tasks run ruff check --diff, which exits 0 on
  unfixable violations — added the plain CI-parity ruff check (#10146)
- development/grafana/AGENTS.md (new): only defined datasource variables,
  sweep drill-down links, regenerate the standalone compose (#10153)
- docs/AGENTS.md: Excalidraw exports use an opaque white background (#9953)

Strengthened in place:
- creating-changelog-entries skill: removed the 'most maintenance still
  gets a fragment' push that contradicted the no-user-facing-effect
  boundary; added lint/typing config cleanups as a named example (#10146)

Paid for by compressing testing.md's nested-dataclass example and
query-pattern.md's duplicated accessor/return-properties sections.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LYisRTn3sPygP55cxbVqzG
(cherry picked from commit ca2190d)
Review follow-ups from #10030's threads, applied on top of the three
cherry-picked harvest runs (#10098, #10145, #10157):

- backend/AGENTS.md: keep the coding-standards list contiguous and let the
  exceptions.md pointer follow it, dropping the duplicated exception-handling
  bullet (cubic, polmichel)
- speckit.opsmill.extract command + skill: error-handling conventions now
  route to dev/guidelines/backend/exceptions.md, and the target list is
  marked as routing examples to re-verify before writing (cubic, polmichel)
- dev/guidelines/backend/python.md: replace the str-satisfies-Sequence
  survival section with a rule against one-or-many unions - prefer
  Sequence[T]/list[T] parameters and let callers wrap (polmichel)
- AGENTS.md: compress the ruff --diff caveat and point at /pre-ci, which
  carries the CI-parity check (polmichel)
- dev/guidelines/repository-organization.md: point the Git Workflow cross
  reference at its #pull-requests anchor (cubic)
- harvesting-review skill 0.8.0: a split or move repoints every inbound
  reference including .agents/skills and .agents/commands routes; a lesson
  whose root cause is a fragile pattern becomes a rule steering away from
  the pattern, not a survival guide; lessons owned by an existing skill
  (creating-changelog-entries, pre-ci, pruning-residues) are routed into
  that skill instead of a parallel dev/ rule. Paid for by compressing the
  sweep's fix-every-hit prose and the report template comments.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@saltas888
saltas888 requested review from a team as code owners August 12, 2026 14:55
@saltas888 saltas888 changed the title docs(guidelines): internal-doc writing style, size discipline, three harvests docs(guidelines): internal-doc writing style, size discipline, six harvest runs Aug 12, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

All reported issues were addressed across 37 files (changes from recent commits).

Shadow auto-approve: would not auto-approve because issues were found.
Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread .agents/skills/harvesting-review/SKILL.md Outdated
Comment thread dev/guidelines/backend/testing.md
Comment thread dev/guides/frontend/writing-unit-tests.md Outdated
Comment thread dev/knowledge/backend/mutations.md
Comment thread development/grafana/AGENTS.md Outdated
Comment thread dev/guidelines/frontend/typescript.md Outdated
Comment thread dev/guides/frontend/writing-component-tests.md Outdated
Comment thread .agents/skills/harvesting-review/SKILL.md
- harvesting-review skill: the sweep greps now include .agents/rules, which
  the prose already claimed; pruning-residues is identified as an org
  skills-plugin skill not vendored in this repo
- mutations.md: the retry decorator also replays on
  Neo.ClientError.Statement.EntityNotFound, not only TransientError
- testing.md: wire the StaleEntryCleaner example (constructor + instantiation)
  so the snippet is reproducible
- writing-component-tests.md: vi.resetAllMocks in the isolation example,
  clearAllMocks leaves mockReturnValue overrides in place
- writing-unit-tests.md: promote Testing Module-Level Singletons out of
  Mocking to its own section, it is about module state, not mocks
- typescript.md: relative link to react.md instead of a backticked absolute
  path
- development/grafana/AGENTS.md: run convert_compose_standalone.py from
  development/, the script resolves default paths against the working
  directory

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

0 issues found across 7 files (changes from recent commits).

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Shadow auto-approve: would not auto-approve. Auto-approval blocked by 1 unresolved issue from previous reviews.

Re-trigger cubic

Conflict: dev/guidelines/backend/python.md — this branch splits Exception
Handling and ASGI Middleware out of the file; develop's copies had meanwhile
gained the best-effort-side-effects subsection (#10103). Kept the split and
ported that subsection into dev/guidelines/backend/exceptions.md, where the
noqa BLE001 section now cross-references it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions github-actions Bot added the type/documentation Improvements or additions to documentation label Aug 12, 2026
@saltas888

Copy link
Copy Markdown
Contributor Author

@polmichel

Do these skills need to reference this exact file? It creates this kind of issue

Repointed them to exceptions.md, and the harvesting skill now requires repointing all inbound references on a split, including .agents, so this shouldn't recur.

reference and either skips the fragment or renders a bogus issue link instead of an orphan entry.
- **Hand-writing the fragment file.** Use `towncrier create` so the name and location are correct.
- **Placing it in a sub-package directory** (e.g. `backend/changelog/`) instead of the configured fragments directory.
- **Naming the fragment after an internal tracker ID** (Jira, Linear, etc.) instead of the GitHub issue number or `+`. Towncrier's `issue_format` turns the fragment name straight into a GitHub issue URL — an internal ticket ID renders a broken link in the shipped changelog.

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.

Just to be sure: do we agree that the following naming is correct since the + is the first character? +ifc-2546-lorem-ipsum.md.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Almost — the + does suppress the issue link so it's safe, but it's missing the type segment (needs .changed.md etc.) or towncrier won't pick it up. The ticket ID never renders in the output either way, so the slug is better spent on a description. Fixed in b7d06e0. Also found two fragments on develop missing the + (IFC-2747.fixed.md, infp-677.changed.md) that will ship broken links — queued a fix for those.

Comment on lines +246 to +250
Every run of this skill only adds. Nothing else revisits what a previous run wrote, so the layer
grows monotonically — a doc entry that was true and useful the week it landed can quietly become
stale, redundant, or wrong, and stays in place forever unless a run like this one checks it. Do this
sweep every time, not as an occasional separate cleanup — it is cheap (a handful of greps, not a
re-read of every doc) and it is what keeps "harvested" from becoming a synonym for "bloated."

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.

Wdyt about explicitly referring to an existing skill? Like "/pruning-residues"?
It would be interesting to see whether this skill uses it without explicit statement, or not.

@polmichel polmichel Aug 13, 2026

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.

I saw your following commit about that

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Already explicit since ae7986f (pushed before this comment) — named in both the routing rule and the apply step. Sounds like your follow-up found the same commit.

green while the optimization silently regresses later. Before trusting a benchmark result on a
perf PR, check that the query/input it drives actually hits the changed code, not just a
similarly-named neighbor.

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.

I'm not sure about this part of the text:

  1. It ties pytest-benchmark to CodSpeed, which is the tool we use to create and interpret the results of pytest-benchmark.
  2. This text says "Do not trust CodSpeed results". In that case, I think we should either write better tests or drop CodSpeed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fair, reworded in b7d06e0. The point isn't that CodSpeed is untrustworthy — it's that a benchmark hitting a different path measures the neighbor, so "no change" is an honest report of the wrong thing. Dropped the tool from that sentence.


**The whole graph is built there, in one pass, before the work starts.** A component that builds a collaborator part-way through a run — when the flow reaches the step that needs it — has the same problem as one that builds it in `__init__`, and is harder to follow because the wiring is spread across the execution. Pass the collaborator in, and pass per-run values to the entry method instead of holding them as constructor state.

**Invalid wiring fails while you build the graph.** Validate the combination at construction and raise; never let a missing or mismatched collaborator surface later as a silently skipped step or a degraded fallback. Where a generic ties two sides together, parameterize both so the mismatch is a type error rather than a runtime discovery — a `Handler[Any]` paired with `Output[Any]` type-checks against anything and defers the failure to the run.

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.

Nice

instead of writing it into `dev/knowledge`/`dev/guidelines`.
instead of writing it into `dev/knowledge`/`dev/guidelines`. The same test applies when the root
cause is a fragile *pattern* rather than a defect: if the honest fix is to stop writing the pattern
(a `str | Sequence[str]` union and its `isinstance` dispatch, for one), the lesson is the one-line

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.

Not sure about this specific example in the text, it feels over-customized for this use case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed, dropped in b7d06e0 — that was the str|Sequence example naming this PR's own case inside a general rule, exactly the over-customization the skill tells authors to avoid elsewhere.

saltas888 and others added 2 commits August 14, 2026 12:53
docs/AGENTS.md's doc-type table and workflow pointed at guides/AGENTS.md and
topics/AGENTS.md. Those were moved to docs/archive/ in #8958 and deleted in
c5acd83; the guidance now lives in dev/guides/docs/writing-a-guide.md and
writing-a-topic.md, already named further down the same file. Also dropped the
two File Structure entries for the deleted files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

All reported issues were addressed across 1 file (changes from recent commits).

Shadow auto-approve: would not auto-approve because issues were found.
Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread docs/AGENTS.md Outdated
- dev/knowledge/backend/testing.md: reframe the benchmark lesson around the
  input rather than the reporting tool. The rule is that a benchmark taking a
  different path measures its neighbour, so "no change" is an honest report of
  the wrong thing; dropped the wording that read as "don't trust CodSpeed"
- .agents/skills/harvesting-review/SKILL.md: drop the str|Sequence example from
  the fragile-pattern guardrail. Naming this PR's own case inside a general rule
  is the over-customization the skill's own style rules forbid
- .agents/skills/creating-changelog-entries/SKILL.md: merge the orphan-prefix
  and tracker-ID mistakes into one bullet stating the filename shape. Answers
  whether +ifc-2546-lorem-ipsum is valid: the + suppresses the issue link so it
  is, but the type segment is required and the ticket ID never renders
- docs/AGENTS.md: fully qualify the topic-guide path, matching #10259

Net -1 line across the four files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

1 issue found across 4 files (changes from recent commits).

Confidence score: 5/5

  • In .agents/skills/creating-changelog-entries/SKILL.md, the documented failure mode for non-numeric fragment names doesn’t match Towncrier’s actual parsing rules, which could mislead contributors and cause avoidable changelog-fragment naming mistakes in future PRs—update the guidance to reflect the \d+/+-orphan behavior configured in this repo.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".agents/skills/creating-changelog-entries/SKILL.md">

<violation number="1" location=".agents/skills/creating-changelog-entries/SKILL.md:90">
P3: The stated failure mode for a non-numeric fragment name is inaccurate. Towncrier only parses the fragment-name issue segment when it is an integer (`\d+`) or a `+`-orphan slug (this project's config sets `orphan_prefix = "+"`). A name like `IFC-2747.fixed.md` matches neither, so Towncrier skips it with a warning rather than running `issue_format` on it — it is dropped from the changelog, not shipped as a GitHub link to a nonexistent issue. The core advice (use an issue number or a `+` slug, don't name fragments after internal ticket IDs) is correct, but the described consequence misleads an agent about what happens: the fragment silently never appears.</violation>
</file>

Shadow auto-approve: would not auto-approve because issues were found.
Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

- **Getting the filename shape wrong.** It is `<issue-or-+slug>.<type>.md`; the type segment is
required, and a file without one is not picked up. Everything before it is either a GitHub issue
number or a `+`-prefixed slug, because `issue_format` turns a bare name straight into a GitHub
issue URL: `IFC-2747.fixed.md` ships a link to an issue that does not exist. The `+` marks the

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 stated failure mode for a non-numeric fragment name is inaccurate. Towncrier only parses the fragment-name issue segment when it is an integer (\d+) or a +-orphan slug (this project's config sets orphan_prefix = "+"). A name like IFC-2747.fixed.md matches neither, so Towncrier skips it with a warning rather than running issue_format on it — it is dropped from the changelog, not shipped as a GitHub link to a nonexistent issue. The core advice (use an issue number or a + slug, don't name fragments after internal ticket IDs) is correct, but the described consequence misleads an agent about what happens: the fragment silently never appears.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .agents/skills/creating-changelog-entries/SKILL.md, line 90:

<comment>The stated failure mode for a non-numeric fragment name is inaccurate. Towncrier only parses the fragment-name issue segment when it is an integer (`\d+`) or a `+`-orphan slug (this project's config sets `orphan_prefix = "+"`). A name like `IFC-2747.fixed.md` matches neither, so Towncrier skips it with a warning rather than running `issue_format` on it — it is dropped from the changelog, not shipped as a GitHub link to a nonexistent issue. The core advice (use an issue number or a `+` slug, don't name fragments after internal ticket IDs) is correct, but the described consequence misleads an agent about what happens: the fragment silently never appears.</comment>

<file context>
@@ -84,12 +84,14 @@ uv run towncrier create -c "Migrated the frontend build to pnpm workspaces" +pnp
+- **Getting the filename shape wrong.** It is `<issue-or-+slug>.<type>.md`; the type segment is
+  required, and a file without one is not picked up. Everything before it is either a GitHub issue
+  number or a `+`-prefixed slug, because `issue_format` turns a bare name straight into a GitHub
+  issue URL: `IFC-2747.fixed.md` ships a link to an issue that does not exist. The `+` marks the
+  entry as an orphan and suppresses the link, so `+ifc-2546-lorem-ipsum.changed.md` is valid — but
+  the ticket ID never appears in the output, so spend the slug on a description instead.
</file context>

cubic flagged these on #10270, the stable extraction; the files are
byte-identical so they were live here too.

The lint claim was factually wrong. Verified against tasks/main.py and
tasks/backend.py: _lint_ruff runs 'uv run ruff check tasks models utilities
python_testcontainers' and backend.ruff runs 'uv run ruff check backend', both
plain. The only --diff is 'ruff format --check --diff', the formatter step. So
the invoke tasks do exit nonzero on an unsuppressed BLE001 inside the paths they
cover, and the real reason the whole-repo check is not redundant is coverage:
CI runs 'ruff check . --exclude python_sdk', so a violation in development/,
tests/, or a root-level script passes locally and fails in CI. Corrected in
.agents/commands/pre-ci.md (both phases) and root AGENTS.md.

- dev/guidelines/documentation.md: the citation example named
  core/regeneration/models.py::TargetSelection, a real module on develop but
  absent from stable. Replaced with a generic some/module.py::SomeClass, which
  is correct on both branches and cannot rot.
- .agents/skills/harvesting-review/SKILL.md: the report template pointed at
  '(§6)' for house style, but §6 is Report; it now points at 'Refine, don't
  accrete'. The rot-sweep glob 'AGENTS.md */AGENTS.md' matched only one level
  deep, missing frontend/app/AGENTS.md and development/grafana/AGENTS.md;
  $(git ls-files '*AGENTS.md') covers all five and skips .venv, which a
  recursive glob would have pulled in.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
saltas888 added a commit that referenced this pull request Aug 14, 2026
Same fixes as on #10030 (757b34f), keeping every file byte-identical across
the two branches.

- the invoke-lint justification was factually wrong: main.lint and backend.ruff
  both run a plain 'ruff check', the only --diff is the formatter's. The real
  reason the whole-repo check matters is coverage, since CI lints everything
  outside the four dirs main.lint covers
- the citation example named a develop-only module; now a generic placeholder
- the report template's '(§6)' house-style pointer aimed at Report
- the rot-sweep glob missed AGENTS.md files two levels deep

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

0 issues found across 4 files (changes from recent commits).

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Shadow auto-approve: would not auto-approve. Auto-approval blocked by 1 unresolved issue from previous reviews.

Re-trigger cubic

PR #10220 (INBOX-29) re-enabled ruff TRY400 across 36 backend logging
sites. Its review was unusually dense - two cubic findings, a rebutted
release-vehicle finding, and a long CI-triage thread - and investigation
turned up five durable gaps.

- cubic-dev-ai flagged graphql/app.py's ClientDisconnect site being
  converted to log.exception when it's a routine, expected condition
  (the traceback only shows the body-read path). The author's own
  research.md stated the criterion ("convert unless the traceback would
  be actively harmful or worthless") and misapplied it on this exact
  site - meaning the criterion existed only in a throwaway per-PR spec
  doc, never in the durable guideline. Added a log.exception vs
  log.error section to exceptions.md with both the worthless case (this
  one) and the harmful case (cross-linked to webhooks.md's
  TracebackSuppressionFilter, the mechanism the same PR's webhook site
  depends on).

- cubic also flagged the same PR's tasks.md citing absolute line numbers
  that its own commits shifted mid-review. That rule already exists
  almost verbatim in documentation.md ("cite the module path and symbol
  only... a spec's own line-numbered citations routinely rot before the
  feature it describes even merges") - so this is "covered but still
  flagged," not missing. Neither speckit-plan nor speckit-tasks, the
  skills that actually generate research.md/plan.md/tasks.md, pointed at
  that rule while enumerating sites. Added a one-line pointer to each.

- The same post-review fix (28/6 to 27/7 conversions) went stale in five
  sibling spec files (plan.md, research.md, tasks.md, alignment-check.md,
  the implementation report) before cubic finished flagging all five
  individually. Root AGENTS.md already tells authors to verify a claim
  against the diff before writing it down; extended that bullet to also
  require propagating a later correction across every file in
  dev/specs/<feature>/ that restates the same figure, not just the one a
  reviewer pointed at.

- cubic asked whether this should target stable instead of develop
  ("repo tooling... cannot affect a running product"). The rebuttal was
  right (the diff converts 27 runtime logging call sites; the closest
  precedents targeted develop) but the underlying policy it invoked -
  "release-vehicle guidance" - doesn't exist anywhere in dev/ or
  AGENTS.md; grepped for it directly. Added the rule both sides were
  actually reasoning from into git-workflow.md's Branch Strategy: a
  tooling/lint/CI change with a runtime source diff targets develop like
  a product change; only a zero-source-diff change targets stable.

- The PR's CI came back red repeatedly on self-hosted-runner exhaustion,
  and each reconcile pass proved out two techniques monitoring-pull-
  requests/SKILL.md didn't have: citing a specific run on the base
  branch as proof a failure predates the PR (rather than an inferred
  "probably pre-existing"), and re-running a failed job on the same SHA
  instead of pushing a fix-attempt commit first, since ci.yml sets
  concurrency.cancel-in-progress: true and a new push cancels the
  experiment. Added both to Phase 2's existing staleness callout.

Not encoded: a Vale spelling-exceptions judgment call (reword a fragment
vs. add a one-off word to the shared vocabulary) - no evidence this
recurs, and the decision is a generic technical-writing tradeoff rather
than an Infrahub-specific gotcha. The "rebase deliberately skipped" call
and the runner-exhaustion diagnosis itself are the existing staleness
rule working correctly and an infra incident, respectively - not new
lessons. Swept the destination layer for citation rot per the skill's
step 5; found none to prune.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015QQtPSrTWwyKv5U4vpVTNZ

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

1 issue found across 6 files (changes from recent commits).

Confidence score: 5/5

  • In dev/guidelines/backend/exceptions.md, the as exc binding in the “good” examples is unused, so copied snippets can trigger Ruff F841 and create confusion against nearby examples that do use exc; remove the unused binding (or use it consistently in logging) to keep the guidance copy-paste safe.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="dev/guidelines/backend/exceptions.md">

<violation number="1" location="dev/guidelines/backend/exceptions.md:123">
P3: Both examples bind `as exc` but never reference `exc`, which trips ruff F841 if copied, and contradicts the file's own `log.error(..., error=str(exc))` examples that do use it. Drop the unused binding so the ✅ Good example is a clean idiom to copy.</violation>
</file>

Shadow auto-approve: would not auto-approve because issues were found.
Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic


```python
# ❌ Bad - a client aborting mid-request is routine; the traceback is non-actionable noise
except ClientDisconnect as exc:

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: Both examples bind as exc but never reference exc, which trips ruff F841 if copied, and contradicts the file's own log.error(..., error=str(exc)) examples that do use it. Drop the unused binding so the ✅ Good example is a clean idiom to copy.

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

<comment>Both examples bind `as exc` but never reference `exc`, which trips ruff F841 if copied, and contradicts the file's own `log.error(..., error=str(exc))` examples that do use it. Drop the unused binding so the ✅ Good example is a clean idiom to copy.</comment>

<file context>
@@ -105,6 +105,32 @@ except Exception as exc:  # noqa: BLE001
+
+```python
+# ❌ Bad - a client aborting mid-request is routine; the traceback is non-actionable noise
+except ClientDisconnect as exc:
+    log.exception("Exception ClientDisconnect in handler")
+
</file context>
Suggested change
except ClientDisconnect as exc:
except ClientDisconnect:

saltas888 added a commit that referenced this pull request Aug 15, 2026
…o stable (#10270)

* docs(skills): bring the harvesting-review skill and its house style to stable

Extracted from #10030 so the next harvest runs with the improved skill instead
of waiting for the develop -> stable release train. Every file here is
byte-identical to #10030's version, so the branches converge rather than drift.

- .agents/skills/harvesting-review/SKILL.md: 'Refine, don't accrete' leads the
  skill and outranks its other rules (measure the target file against its size
  range, compress or split one at its limit, repoint every inbound reference on
  a move, cut what the new rule supersedes, report added/removed lines). Adds
  the per-run rot sweep that fixes stale citations and defect-snapshot notes in
  the same PR, and routes a lesson into the skill that owns its workflow.
- dev/guidelines/documentation.md: the 'For Internal Docs' writing style the
  skill points at, plus the no-line-number and relative-measurement rules. The
  skill's house-style pointer would dangle without this.
- .agents/commands/pre-ci.md: plain 'ruff check' CI-parity step, since the
  invoke tasks run 'ruff check --diff' and exit 0 on violations with no autofix.
- .agents/skills/monitoring-pull-requests/SKILL.md: don't escalate from a stale
  'commits behind' count.

Deliberately not included, because they would not be correct on stable yet:
the speckit-extract repoint (targets dev/guidelines/backend/exceptions.md,
which only exists on develop), and the .agents/rules/* and
creating-changelog-entries changes (their stable copies have diverged, and
#10259 already edits them against stable).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: apply cubic's four findings

Same fixes as on #10030 (757b34f), keeping every file byte-identical across
the two branches.

- the invoke-lint justification was factually wrong: main.lint and backend.ruff
  both run a plain 'ruff check', the only --diff is the formatter's. The real
  reason the whole-repo check matters is coverage, since CI lints everything
  outside the four dirs main.lint covers
- the citation example named a develop-only module; now a generic placeholder
- the report template's '(§6)' house-style pointer aimed at Report
- the rot-sweep glob missed AGENTS.md files two levels deep

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

group/backend Issue related to the backend (API Server, Git Agent) type/documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants