Skip to content

docs: harvest review lessons from PRs reviewed 2026-07-31 to 2026-08-07 - #10157

Closed
saltas888 wants to merge 1 commit into
docs/harvest-week-2026-08-06from
claude/cool-pasteur-68vjre
Closed

docs: harvest review lessons from PRs reviewed 2026-07-31 to 2026-08-07#10157
saltas888 wants to merge 1 commit into
docs/harvest-week-2026-08-06from
claude/cool-pasteur-68vjre

Conversation

@saltas888

@saltas888 saltas888 commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Why

A /harvesting-review run over review activity 2026-07-31 → 2026-08-07. #10145 already harvested the window's merged PRs, so this run reads what it skipped: the week's open, draft, and closed-superseded PRs plus merges it postdates — where reviewers did much of their teaching this week, and where lessons otherwise die when the PR closes unmerged.

Stacked on #10145#10098#10030develop, so its unmerged doc edits are the dedup baseline and the stacks don't collide.

Non-goals: no code changes — latent code issues the investigation confirmed are flagged below as follow-up candidates, not fixed here.

Source PRs

29 PRs from the window were read (resolved and unresolved threads both); the 17 already covered by #10145/#10098 were excluded up front. PRs that produced lessons:

PR What review surfaced
#10121 ogenstad ×2 + author self-review: @retry_db_transaction on the whole create mutation replays an already-committed transaction (duplicate node); a marker + type: ignore added to production code so a test could see it — reviewer supplied the ast-based wiring test instead
#10132 cubic ×4 (author-validated): a READ query with insert_limit = False and no own LIMIT never finishes paging, plus the author's ORDER-BY-disjointness addition; one bad item aborting a multi-branch migration; batches sized by node count when edges bound memory; a delete assertion reading the wrong branch
#10105 ajtmccarty (changes requested): fix the schema-migration layer, don't lazily repair in save() — the PR was closed for #10113. cubic: post-construction changelog assignment bypasses the Password masking validator
#9805 ajtmccarty: a Cypher filter on n.kind silently matches zero generics — generics exist only as labels; the fix typed the query input as list[NodeSchema] so misuse fails in CI
#10113 cubic (author-validated): grouping migrations into a "phase" is not serialization — InfrahubBatch runs everything concurrently
#10146/#10147 ogenstad: no changelog fragment for a change that touches no source code. Author root-cause: invoke lint tasks run ruff check --diff, blind to unfixable violations CI catches
#10133 cubic (author-validated): integration tests polling for async outcomes instead of fixed sleeps
#10137 cubic + ogenstad's concession: logic that "needs the full integration fixture" was a pure function over constructible data all along
#10122 cubic: a new Settings field reached the generated, CI-validated root compose file but not the hand-maintained development/ anchor nothing validates
#8594 ogenstad (resolution landed this week): no REST endpoint to serve an SDK method — extend the existing GraphQL query; the SDK method was removed instead
#10153 cubic: drill-down links still referencing an undefined $datasource variable after the query targets were fixed
#9953 BeArchiTek: opaque-white Excalidraw exports confirmed as the 9-of-16 majority convention in docs/docs/media/ (verified here)

Read with nothing durable to harvest: #10141, #10154, #10149, #10144, #10099, #10124, #10080, #10089, #10081, #7569, #9631/#9635/#8902/#10148/#10101/#10142/#10153-approvals (activity pre-window, bare approvals, or bot no-issue reviews).

What changed

New rules/knowledge (each verified against the code before writing — the retry semantics against database/__init__.py/create.py/mutations/main.py, the paging loop against core/query/__init__.py, the masking validator against changelog/models.py, the label model against core/node/__init__.py, the compose asymmetry against tasks/release.py + ci.yml, the dashboards against the JSON on develop):

  • dev/knowledge/backend/mutations.mdTransaction Retry: wrap only rollback-able scopes, never post-commit reads; skip under db.is_transaction; watch nested retried callers
  • dev/knowledge/backend/query-pattern.md — the two READ-pagination failure modes (insert_limit = False without own LIMIT; missing total-order ORDER BY)
  • dev/guides/backend/creating-migrations.md — fix data bugs in the migration layer, not runtime save paths; per-item try/collect into MigrationResult.errors; batch by the memory-bounding unit
  • dev/knowledge/backend/database-schema.mdkind property is concrete-only, generics are labels; type concrete-only query inputs as list[NodeSchema]
  • dev/knowledge/backend/events.md — changelog models mask secrets only at construction; assignment after construction leaks them
  • dev/knowledge/backend/async-tasks.mdInfrahubBatch is concurrent, not ordered
  • dev/guidelines/backend/testing.md + .agents/rules/testing-python.md — wiring tests parse source (ast + inspect) instead of instrumenting production code; poll-don't-sleep for async effects; branch-attributable removal assertions; extract-a-pure-function before claiming the cheap tier is unreachable
  • dev/guidelines/backend/checklist.mdConfiguration section: the generated/hand-maintained compose split
  • backend/AGENTS.md — new REST endpoints join Ask First; creating-migrations.md routed in the Guides list (it was missing entirely — nothing ever loaded it)
  • .agents/commands/pre-ci.md — plain ruff check CI-parity step; corrected the claim that the invoke tasks report unfixable violations
  • development/grafana/AGENTS.md (new) — defined datasource variables only; sweep link surfaces; regenerate the standalone compose
  • docs/AGENTS.md — opaque-white Excalidraw exports

Strengthened where it already lived (rule existed, author still tripped): the creating-changelog-entries skill said "no fragment for changes with no user-facing effect" three lines below a parenthetical pushing "(most internal maintenance still gets a housekeeping fragment)" — the author of #10146 cited exactly that reflex. The parenthetical now defers to the boundary, and lint/typing config cleanups are a named example.

Paid for by: compressing testing.md's nested-dataclass example and query-pattern.md's duplicated accessor/return-properties sections. Net +143/−91; testing.md 389 and query-pattern.md 430 (was 459 — still over its 200–400 range; the ## Internals split remains its own change, as #10145 noted).

Not codified, flagged as follow-ups (investigation confirmed them real in code):

How to review

Every lesson cites its source PR above; the highest-leverage edits are mutations.md (Transaction Retry) and the query-pattern.md pagination block, both bugs-in-waiting rather than style. Bot-sourced lessons were held to the skill's lower-confidence bar: kept only where the author landed the fix or the claim was re-verified against the code here.

How to test

uv run invoke docs.lint

Docs-only. CI's markdown-lint and Vale cover docs/**; relative links and anchors in the edited dev/ files checked locally (#return-labels target exists; lychee's dev/**/*.md glob covers the edited files).

Impact & rollout

  • Backward compatibility: docs-only, no runtime impact
  • Deployment notes: safe to merge once the stack below it lands

Checklist

  • Tests added/updated (n/a — docs-only)
  • Changelog entry added (n/a — internal docs, no user-facing effect)
  • External docs updated (n/a)
  • Internal .md docs updated (internal knowledge and AI code tools knowledge)
  • I have reviewed AI generated content

🤖 Generated with Claude Code

https://claude.ai/code/session_01LYisRTn3sPygP55cxbVqzG


Generated by Claude Code

Review in cubic

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
@github-actions github-actions Bot added type/documentation Improvements or additions to documentation group/backend Issue related to the backend (API Server, Git Agent) labels Aug 7, 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.

2 issues found across 14 files

Confidence score: 4/5

  • In dev/guidelines/backend/testing.md, the cited “house example” (backend/tests/unit/workflows/test_flow_session_convention.py) appears to be fabricated or mismatched with the actual code, which can mislead contributors into copying a non-existent pattern and erode trust in the testing guide — replace it with a real, verifiable example from the repo or remove the claim.
  • In dev/knowledge/backend/mutations.md, the retry_db_transaction retry trigger is documented too narrowly versus the implementation in backend/infrahub/database/__init__.py, so readers may misunderstand when retries happen and design mutation logic incorrectly — align the docs with the full set of retry conditions.
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/knowledge/backend/mutations.md">

<violation number="1" location="dev/knowledge/backend/mutations.md:87">
P3: This section documents when `retry_db_transaction` replays the wrapped function, but the trigger is described only as 'when Neo4j raises a TransientError'. The decorator in `backend/infrahub/database/__init__.py` actually catches both `TransientError` and `ClientError`, and only re-raises a `ClientError` when its code is not `Neo.ClientError.Statement.EntityNotFound` — so it also retries on that EntityNotFound condition. Since this knowledge doc exists precisely to encode retry semantics for future contributors, consider naming both trigger paths so someone reading it doesn't assume EntityNotFound is a non-retried failure.</violation>
</file>

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

<violation number="1" location="dev/guidelines/backend/testing.md:262">
P2: Custom agent: **Flag AI Slop and Fabricated Changes**

The docs claim `backend/tests/unit/workflows/test_flow_session_convention.py` is the house example of parsing decorator names and arguments via `ast.parse(inspect.getsource(module))`, but the file does not check decorators at all. It verifies `service.database` session-scoping conventions in Prefect flows. Correct the description to match the file's actual purpose so the example is accurate.</violation>
</file>

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

Re-trigger cubic

To verify *wiring* — the right decorator applied, a convention held across a module — parse the
source instead of instrumenting it: an `ast.parse(inspect.getsource(module))` test reads decorator
names and arguments off the tree with zero production hooks.
`backend/tests/unit/workflows/test_flow_session_convention.py` is the house example. Behavioral

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.

P2: Custom agent: Flag AI Slop and Fabricated Changes

The docs claim backend/tests/unit/workflows/test_flow_session_convention.py is the house example of parsing decorator names and arguments via ast.parse(inspect.getsource(module)), but the file does not check decorators at all. It verifies service.database session-scoping conventions in Prefect flows. Correct the description to match the file's actual purpose so the example is accurate.

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

<comment>The docs claim `backend/tests/unit/workflows/test_flow_session_convention.py` is the house example of parsing decorator names and arguments via `ast.parse(inspect.getsource(module))`, but the file does not check decorators at all. It verifies `service.database` session-scoping conventions in Prefect flows. Correct the description to match the file's actual purpose so the example is accurate.</comment>

<file context>
@@ -282,9 +248,20 @@ If you find yourself wanting to mock:
+To verify *wiring* — the right decorator applied, a convention held across a module — parse the
+source instead of instrumenting it: an `ast.parse(inspect.getsource(module))` test reads decorator
+names and arguments off the tree with zero production hooks.
+`backend/tests/unit/workflows/test_flow_session_convention.py` is the house example. Behavioral
+coverage (does the decorator retry?) belongs on the decorator's own tests; the wiring test only
+proves it is attached.
</file context>


## Transaction Retry

`retry_db_transaction` (in `backend/infrahub/database/__init__.py`) replays the *entire* wrapped

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 section documents when retry_db_transaction replays the wrapped function, but the trigger is described only as 'when Neo4j raises a TransientError'. The decorator in backend/infrahub/database/__init__.py actually catches both TransientError and ClientError, and only re-raises a ClientError when its code is not Neo.ClientError.Statement.EntityNotFound — so it also retries on that EntityNotFound condition. Since this knowledge doc exists precisely to encode retry semantics for future contributors, consider naming both trigger paths so someone reading it doesn't assume EntityNotFound is a non-retried failure.

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

<comment>This section documents when `retry_db_transaction` replays the wrapped function, but the trigger is described only as 'when Neo4j raises a TransientError'. The decorator in `backend/infrahub/database/__init__.py` actually catches both `TransientError` and `ClientError`, and only re-raises a `ClientError` when its code is not `Neo.ClientError.Statement.EntityNotFound` — so it also retries on that EntityNotFound condition. Since this knowledge doc exists precisely to encode retry semantics for future contributors, consider naming both trigger paths so someone reading it doesn't assume EntityNotFound is a non-retried failure.</comment>

<file context>
@@ -82,6 +82,22 @@ mutate_upsert()
 
+## Transaction Retry
+
+`retry_db_transaction` (in `backend/infrahub/database/__init__.py`) replays the *entire* wrapped
+function when Neo4j raises a `TransientError`. Its placement is therefore semantics-bearing, not
+decoration:
</file context>

@codspeed-hq

codspeed-hq Bot commented Aug 7, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 31.52%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 2 improved benchmarks
✅ 10 untouched benchmarks
⏩ 1 skipped benchmark1

Performance Changes

Benchmark BASE HEAD Efficiency
test_base_schema_duplicate_CoreProposedChange 2.2 ms 1.5 ms +49.68%
test_schemabranch_duplicate 7.7 ms 6.7 ms +15.57%

Tip

Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.


Comparing claude/cool-pasteur-68vjre (ca2190d) with stable (5ae546a)2

Open in CodSpeed

Footnotes

  1. 1 benchmark was skipped, so the baseline result was used instead. If it was deleted from the codebase, click here and archive it to remove it from the performance reports.

  2. No successful run was found on docs/harvest-week-2026-08-06 (4c4a19d) during the generation of this report, so stable (5ae546a) was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

saltas888 added a commit that referenced this pull request Aug 12, 2026
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

Copy link
Copy Markdown
Contributor Author

Cherry-picked into #10030 as 7f8e010, with one reconciliation: develop's query-pattern.md had meanwhile grown overlapping self-paging sections, so only this PR's genuinely-new point (total-order ORDER BY for auto-pagination) was folded in. Review follow-ups in ae7986f. Closing in favor of #10030 so the harvest series lands as one PR.

@saltas888 saltas888 closed this Aug 12, 2026
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.

2 participants