Skip to content

chore(devex): adopt the facade wiring-couplings doctrine - #71486

Merged
webjunkie merged 10 commits into
masterfrom
chore/devex-facade-wiring-doctrine
Jul 17, 2026
Merged

chore(devex): adopt the facade wiring-couplings doctrine#71486
webjunkie merged 10 commits into
masterfrom
chore/devex-facade-wiring-doctrine

Conversation

@webjunkie

Copy link
Copy Markdown
Contributor

Problem

turbo-discover skips the full Django suite for products declaring backend:contract-check. That skip is only sound if nothing inside the product can change what the outside world observes without a watched file changing. Several facades hand out behavioral classes defined in unwatched internals. Live case: MetricsQueryRunner travels out through the metrics facade, is defined in an unwatched module, and core's query dispatch instantiates it, so an implementation change could sail through with the suite skipped.

#71127 tried to fix this with an AST detector keyed on __all__ plus per-file turbo.json widenings. Review rounds kept finding holes (lazy facades, aliases, duplicate names, glob semantics), and dozens of facade modules declare no __all__ at all, so the inspection list could not be trusted. The deeper gap: architecture.md never said what may cross a facade, which is how facades accumulated ORM models, ad hoc classes, and a test mixin. Shopify's Packwerk public folders famously rotted the same way once location granted publicness without an API shape.

Changes

Doctrine first, in a new "Wiring couplings" section of products/architecture.md (plus the isolation rule):

  • data crosses as frozen dataclasses; behavior crosses only as an implementation of a core-owned base (QueryRunner, MaxTool, Temporal defns, @shared_task) defined in a wiring location (backend/hogql_queries/, backend/max_tools.py, backend/temporal/, backend/tasks/) that stays in the contract-check inputs
  • Django models never cross, with two named class-identity registry carve-outs (team extension, file-system unfiled)
  • anything else behavioral loses the skip
  • registration points should validate with issubclass - no static Python tool can check what an object is, only where imports point (MaxTool already does this and is the reference)

State changes make the doctrine true. Contract-check inputs are now binary: clean products narrow to facade + presentation + routes + wiring locations, and products whose facades still hand out unsanctioned classes drop their turbo.json override entirely and watch everything (fail-safe) until the classes move - no more hand-maintained per-file glob lists. Three files move into wiring locations, six pure error types move into facade contracts, and demo loses backend:contract-check outright: its matrix classes implement no core interface and are driven straight from signup.

Lint closes the loop: hogli product:lint now flags a class a facade re-exports from outside the wiring locations, and a wiring location that exists but is not watched. Detection is keyed on import statements rather than __all__ (including PEP 562 lazy maps and the Foo as Foo idiom that ruff F401 deliberately ignores). Leaks gate narrowing, not the script, so watch-everything stays a valid interim state and the five interim products (tasks, warehouse_sources, data_modeling, data_warehouse, endpoints) earn narrowing back by relocating their types.

How did you test this code?

All automated, run by the agent:

  • hogli product:lint --all: all 67 products pass
  • hogli test tools/hogli-commands: 1206 passed (one pre-existing environment-sensitive devbox test fails identically without these changes)
  • tach check, lint-imports, ruff, hogli ci:preflight --fix all clean
  • targeted product tests for every moved or edited module (metrics runner, error tracking tools, data warehouse fixer, streamlit apps runtime, customer analytics custom properties)

New tests in hogli-commands cover the detection shapes (eager, lazy, self-alias, TYPE_CHECKING, third-party, one-hop package re-export), wiring-location coverage including the flat-file form, the narrowed-vs-broad gating split, and the carve-outs. The regression they catch that nothing else did: a detector miss silently re-arms an unsound suite skip.

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

Docs update

products/architecture.md and .claude/rules/product-isolation.md are updated in this PR.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Written by Claude (Claude Code), human-directed throughout. This replaces #71127 after we concluded its __all__-keyed detector could not be made trustworthy (most facade modules have no __all__, so the gate certified only the advertised subset of the surface). Skills invoked: /simplify (four-angle review; its findings were applied, including a copy-paste drift bug between coverage helpers) plus the repo test-writing conventions for the new tests.

Decisions worth knowing at review: leaks gate narrowing rather than eligibility, because changing eligibility would force the five interim products to drop their script instead of watching everything; DRF viewsets are deliberately not on the approved interface list (they are the presentation channel with its own rules and never pass through the facade); per-file turbo widenings were rejected as fail-dangerous in favor of narrow-or-nothing. A codebase census informed the approved-interface list; only MaxTool validates at registration today, so bouncer hardening for the Temporal worker and the file-system model map is a named follow-up, as is a colocated marker mechanism if the carve-out list ever needs a third entry.

architecture.md said facade/api.py is the only importable file, while
tach.toml and the isolation rules already expose the whole facade package
with capability submodules — the doc lost that argument long ago. Catch it
up and close the gap it left: nothing constrained WHAT may cross, which is
how facades started handing out ORM models, ad-hoc classes, and even a
test mixin. Shopify's Packwerk public folders rotted into a catch-all
drawer for exactly this reason: location granted publicness without an
API shape.

The doctrine: data crosses as frozen dataclasses; behavior crosses only
as an implementation of a core-owned base (QueryRunner, MaxTool, Temporal
defns, shared_task) defined in a wiring location that stays in the
contract-check inputs; registration points validate with issubclass,
since no static Python tool can check what an object is — tach and
import-linter only see the import graph. Django models never cross (a
model cannot be narrowed); two class-identity registries are named
carve-outs. Anything else behavioral loses the contract-check skip.
Per-file input widenings bet on a hand-maintained list being complete —
fail-dangerous, and the awkward lists were multiplying. Contract-check
inputs are binary now: doctrine-clean products narrow to facade,
presentation, routes, and their wiring locations; everything else drops
the turbo.json override entirely and inherits the watch-everything
default, so any change re-runs the full suite until the product earns
the skip back.

Moves that make products clean: MetricsQueryRunner into
backend/hogql_queries/, SearchErrorTrackingIssuesTool and
HogQLQueryFixerTool into backend/max_tools.py — each already wears a
core-owned base, only the file lived outside the wiring location. Pure
error types move to facade/contracts.py (streamlit_apps app-runtime
errors, customer_analytics InvalidCustomPropertyOptions).

demo is demoted outright: its facade hands out MatrixManager and the
matrix classes with no core-owned interface, driven from signup and the
demo management commands. No sound skip exists for that shape.

tasks, warehouse_sources, data_modeling, data_warehouse and endpoints
keep their script but watch everything — their facades still hand out
classes from non-wiring modules (several with live consumers, two pinned
by migration files), so narrowing waits until those move.
hogli product:lint now reads what a facade actually hands out instead of
trusting where files sit. Two checks: a class a facade module re-exports
from outside the wiring locations blocks narrowing — a hard error when
the product is narrowed, a warning while it watches everything — and a
wiring location that exists but isn't in the contract-check inputs is an
error, mirroring the routes rule.

Detection is keyed on import statements, not __all__: any module-level
binding in a facade module is importable under tach's facade exposure,
advertised or not. Pure re-export modules count everything they import;
function-bearing modules count __all__ plus the Foo-as-Foo self-alias
idiom (the shape ruff F401 deliberately ignores); PEP 562 lazy maps are
read as the import map they are. Coverage stays prefix-based — wiring
locations are directory conventions, so no glob engine.

The gate deliberately targets narrowing, not the script: script plus
broad inputs is inert (everything re-runs), so it stays a valid interim
state, and the missing-narrowing nag is suppressed while a leak blocks
the narrowing it would demand. Two class-identity registry carve-outs
(team extension, file-system unfiled) are allowed but their defining
modules must stay watched.
@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

🤖 CI report

⚠️ Bundle size — 🔺 +90 B (+0.0%)

Uncompressed size of every built .js bundle, compared against the base branch.

Total: 64.85 MiB · 🔺 +90 B (+0.0%)

No file changed by more than 1000 B.

Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report

Eager graph — within budget

How much code each root ships on the eager path — downloaded and parsed before the surface is interactive. Measured from the esbuild output chunks (post-tree-shake, static imports only); lazy import() / React.lazy chunks are not counted.

Root Eager (shipped) Δ vs base Budget
entry (logged-out pages, app bootstrap)
src/index.tsx
1.23 MiB · 22 files no change ███░░░░░░░ 28.6% of 4.29 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
8.15 MiB · 2,988 files no change █████████░ 88.1% of 9.25 MiB

🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx

Largest files eagerly shipped from src/index.tsx
Size File
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
24.6 KiB ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js
6.3 KiB ../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js
4.5 KiB ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js
3.9 KiB ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js
1.4 KiB ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js
1.3 KiB src/RootErrorBoundary.tsx
912 B ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js
789 B src/scenes/ChunkLoadErrorBoundary.tsx
762 B src/index.tsx
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
Size File
281.3 KiB ../node_modules/.pnpm/posthog-js@1.404.0/node_modules/posthog-js/dist/rrweb.js
267.7 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
235.5 KiB src/taxonomy/core-filter-definitions-by-group.json
223.2 KiB ../node_modules/.pnpm/posthog-js@1.404.0/node_modules/posthog-js/dist/module.js
164.0 KiB src/queries/validators.js
154.3 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
105.8 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
93.2 KiB ../packages/quill/packages/quill/dist/index.js

Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of #32479

Dist folder size — 🔺 +950 B (+0.0%)

Total size of the built frontend/dist folder (all assets), compared against the base branch.

Total: 1336.73 MiB · 🔺 +950 B (+0.0%)

⚠️ Backend coverage — 78.0% of changed backend lines covered — 37 uncovered

🧪 Backend test coverage

Patch coverage — changed backend lines (products + core): ████████████████░░░░ 78.0% (134 / 171)

File Patch Uncovered changed lines
products/error_tracking/backend/facade/tools.py 0.0% 10
products/streamlit_apps/backend/tasks/tasks.py 0.0% 51–52
products/warehouse_sources/backend/temporal/data_imports/sources/tests/test_ci_core_coupled_sources.py 60.0% 141–144
products/error_tracking/backend/max_tools.py 79.0% 279, 312–315, 322–327, 429–430, 452–454, 456–465, 467, 479–481

🤖 Agents: add a test covering the lines above, or note why under "How did you test this code?". Machine-readable gap list: the patch-coverage artifact on this run (gh run download 29594072949 -n patch-coverage), or the coverage-data block at the end of this comment.

Per-product line coverage (touched products)
Product Coverage Lines
platform_features ██░░░░░░░░░░░░░░░░░░ 12.1% 7 / 58
batch_exports ████████░░░░░░░░░░░░ 39.7% 8,416 / 21,225
demo ███████████░░░░░░░░░ 56.2% 1,497 / 2,663
warehouse_sources_queue ████████████░░░░░░░░ 59.2% 148 / 250
tasks █████████████░░░░░░░ 67.1% 25,922 / 38,649
data_tools ██████████████░░░░░░ 70.0% 63 / 90
ai_gateway ███████████████░░░░░ 75.0% 9 / 12
signals ████████████████░░░░ 79.2% 19,224 / 24,265
data_modeling ████████████████░░░░ 80.0% 4,834 / 6,045
cdp ████████████████░░░░ 80.7% 3,118 / 3,864
wizard ████████████████░░░░ 82.5% 772 / 936
notebooks █████████████████░░░ 84.8% 6,903 / 8,141
cohorts █████████████████░░░ 86.2% 4,065 / 4,717
agent_platform █████████████████░░░ 86.4% 3,807 / 4,405
actions █████████████████░░░ 86.6% 717 / 828
product_tours █████████████████░░░ 87.5% 1,266 / 1,447
exports ██████████████████░░ 88.4% 6,933 / 7,842
visual_review ██████████████████░░ 88.5% 5,565 / 6,287
business_knowledge ██████████████████░░ 88.5% 4,400 / 4,969
conversations ██████████████████░░ 89.0% 16,183 / 18,186
mcp_analytics ██████████████████░░ 89.2% 2,514 / 2,819
engineering_analytics ██████████████████░░ 89.3% 5,407 / 6,053
dashboards ██████████████████░░ 89.4% 5,847 / 6,540
error_tracking ██████████████████░░ 89.9% 9,758 / 10,854
alerts ██████████████████░░ 89.9% 4,045 / 4,499
early_access_features ██████████████████░░ 90.1% 1,031 / 1,144
streamlit_apps ██████████████████░░ 90.4% 2,501 / 2,767
slack_app ██████████████████░░ 90.6% 9,018 / 9,955
links ██████████████████░░ 90.6% 183 / 202
marketing_analytics ██████████████████░░ 90.8% 11,514 / 12,684
stamphog ██████████████████░░ 91.0% 3,993 / 4,387
product_analytics ██████████████████░░ 91.3% 5,757 / 6,304
mcp_store ██████████████████░░ 91.8% 3,685 / 4,012
managed_migrations ██████████████████░░ 91.9% 908 / 988
data_warehouse ██████████████████░░ 92.4% 18,626 / 20,158
notifications ███████████████████░ 92.7% 1,031 / 1,112
web_analytics ███████████████████░ 92.7% 13,535 / 14,597
workflows ███████████████████░ 92.8% 5,482 / 5,909
ai_observability ███████████████████░ 92.8% 14,916 / 16,077
surveys ███████████████████░ 93.0% 5,724 / 6,157
posthog_ai ███████████████████░ 93.2% 1,322 / 1,418
approvals ███████████████████░ 93.3% 3,395 / 3,640
reminders ███████████████████░ 93.4% 468 / 501
tracing ███████████████████░ 93.4% 2,545 / 2,724
legal_documents ███████████████████░ 94.1% 1,568 / 1,667
endpoints ███████████████████░ 94.1% 8,606 / 9,143
revenue_analytics ███████████████████░ 94.5% 3,598 / 3,809
skills ███████████████████░ 94.5% 2,881 / 3,049
messaging ███████████████████░ 94.5% 2,530 / 2,677
review_hog ███████████████████░ 94.6% 6,789 / 7,174
logs ███████████████████░ 95.2% 9,439 / 9,911
experiments ███████████████████░ 95.7% 24,417 / 25,527
replay_vision ███████████████████░ 95.8% 13,776 / 14,383
growth ███████████████████░ 95.8% 2,837 / 2,960
annotations ███████████████████░ 96.2% 732 / 761
feature_flags ███████████████████░ 96.3% 16,172 / 16,794
warehouse_sources ███████████████████░ 96.4% 283,692 / 294,224
user_interviews ███████████████████░ 96.4% 2,242 / 2,325
access_control ███████████████████░ 96.8% 849 / 877
customer_analytics ███████████████████░ 97.2% 7,482 / 7,700
data_catalog ███████████████████░ 97.4% 2,304 / 2,365
analytics_platform ████████████████████ 98.0% 2,102 / 2,145
metrics ████████████████████ 98.2% 2,404 / 2,448
pulse ████████████████████ 98.4% 2,017 / 2,049
live_debugger ████████████████████ 99.2% 613 / 618
field_notes ████████████████████ 99.4% 158 / 159

Report-only. Patch coverage = changed backend lines covered vs origin/master. Sorted lowest first.
Known gaps: lines covered only by Temporal tests show as uncovered; core line numbers may drift if master changed the same file.

@webjunkie
webjunkie marked this pull request as ready for review July 16, 2026 11:05
Copilot AI review requested due to automatic review settings July 16, 2026 11:05
@trunk-io

trunk-io Bot commented Jul 16, 2026

Copy link
Copy Markdown

Static BadgeStatic BadgeStatic BadgeStatic Badge

View Full Report ↗︎Docs

@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested review from a team, MattBro, ablaszkiewicz, cat-ph, fercgomes, hpouillot and rafaeelaudibert and removed request for a team July 16, 2026 11:05
@pr-assigner-resolver-posthog

Copy link
Copy Markdown

👀 Auto-assigned reviewers

These soft owners were skipped because their changes are minor, or the reviewer list was getting long. Nothing blocks merge, so self-assign if you'd like a look:

  • @PostHog/team-warehouse-sources (products/warehouse_sources/product.yaml)
  • @PostHog/team-managed-warehouse (products/data_warehouse/product.yaml)
  • @PostHog/team-data-modeling (products/data_modeling/product.yaml, products/endpoints/product.yaml)
  • @PostHog/logs (products/metrics/product.yaml)
  • @PostHog/team-posthog-code (products/tasks/product.yaml)

Soft owners come from each directory's owners.yaml and each product's product.yaml (resolved nearest-file-wins). The locator after each owner is the file that decided it. Generated files and lockfiles are ignored when deciding ownership.

Copilot AI 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.

Pull request overview

Updates PostHog’s product-isolation “contract-check” doctrine to make turbo’s Django-suite skipping sound when facades re-export behavior, by codifying where behavior is allowed to live (“wiring locations”) and enforcing it via hogli product:lint, with several products adjusted to either watch the right wiring paths or fall back to watch-everything.

Changes:

  • Add “Wiring couplings” doctrine and align product-isolation docs accordingly.
  • Add AST-based detection + lint gating for facade class re-exports outside wiring locations, plus coverage checks ensuring existing wiring locations are watched by narrowed turbo.json.
  • Move/reshape a few product entry points (query runners, Max tools, error types) and adjust turbo.json inputs (or remove narrowing) to match the doctrine.

Reviewed changes

Copilot reviewed 38 out of 41 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tools/hogli-commands/hogli_commands/tests/test_checks.py Adds tests for facade class-leak detection and garage coverage gating.
tools/hogli-commands/hogli_commands/product/isolation.py Implements facade re-export detection, garage/carve-out coverage checks, and updated narrowing logic.
tools/hogli-commands/hogli_commands/product/checks.py Surfaces and enforces wiring-coupling violations in product lint output and script gating.
tools/hogli-commands/hogli_commands/product/ast_helpers.py Adds AST helpers for __all__, lazy re-export maps, import-from harvesting, and top-level function detection.
products/warehouse_sources/turbo.json Removes narrowing override (watch everything).
products/visual_review/turbo.json Adds backend/tasks/** to contract-check inputs.
products/tasks/turbo.json Removes narrowing override (watch everything).
products/streamlit_apps/turbo.json Adds backend/tasks/** to contract-check inputs.
products/streamlit_apps/backend/tasks/tasks.py Switches concurrency error import to facade contracts.
products/streamlit_apps/backend/logic/app_runtime.py Moves error types out to facade contracts.
products/streamlit_apps/backend/facade/contracts.py Adds AppRuntimeError and AppRuntimeConcurrencyError to contract surface.
products/streamlit_apps/backend/facade/api.py Imports error types from facade contracts rather than logic.
products/metrics/turbo.json Adds wiring locations (hogql_queries/**, tasks/**) to inputs.
products/metrics/backend/tests/test_metrics_query_runner.py Updates import to new hogql_queries location.
products/metrics/backend/hogql_queries/metrics_query_runner.py New QueryRunner implementation living under wiring location.
products/metrics/backend/facade/queries.py Re-exports runner from wiring location.
products/legal_documents/turbo.json Adds backend/tasks/** to contract-check inputs.
products/error_tracking/turbo.json Adds wiring locations (hogql_queries, max_tools, temporal, tasks) to inputs.
products/error_tracking/backend/test/test_search_issues.py Updates tool import path to wiring location (max_tools.py).
products/error_tracking/backend/max_tools.py Moves Search tool implementation into wiring location module.
products/error_tracking/backend/logic/tools/test/init.py Maintains/adjusts test package structure after tool move.
products/error_tracking/backend/logic/tools/search_issues.py Removes old tool implementation module.
products/error_tracking/backend/logic/tools/init.py Removes old re-export barrel.
products/error_tracking/backend/facade/tools.py Updates facade re-export to new wiring location module.
products/engineering_analytics/turbo.json Adds backend/tasks/** to contract-check inputs.
products/endpoints/turbo.json Removes narrowing override (watch everything).
products/demo/turbo.json Removes narrowing override (watch everything).
products/demo/package.json Drops backend:contract-check script (no longer isolated).
products/demo/backend/facade/contracts.py Updates rationale to state demo is not isolated due to behavioral facade surface.
products/data_warehouse/turbo.json Removes narrowing override (watch everything).
products/data_warehouse/backend/tests/api/test_fix_hogql.py Updates imports/mocks to new wiring location module.
products/data_warehouse/backend/test/test_hogql_fixer_ai.py Updates imports to new wiring location module.
products/data_warehouse/backend/max_tools.py Introduces HogQL fixer tool implementation in wiring location module.
products/data_warehouse/backend/facade/api.py Updates lazy facade map entry to point to max_tools.
products/data_modeling/turbo.json Removes narrowing override (watch everything).
products/customer_analytics/turbo.json Adds wiring locations + carve-out module to contract-check inputs.
products/customer_analytics/backend/logic/custom_property_definitions.py Moves exception type import to facade contracts.
products/customer_analytics/backend/facade/contracts.py Adds InvalidCustomPropertyOptions to contract surface.
products/customer_analytics/backend/facade/api.py Re-exports exception via facade contracts (no longer from logic).
products/architecture.md Adds “Wiring couplings” doctrine; clarifies facade package surface.
.claude/rules/product-isolation.md Updates agent guidance to match wiring-couplings doctrine.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tools/hogli-commands/hogli_commands/product/isolation.py Outdated
Comment on lines +469 to +474
def _format_date(self, date_value) -> str:
"""Format a date value for display in UTC."""
if not date_value:
return ""
from datetime import datetime

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.

Fixed — merged into the module-level datetime import. The inline import traveled along with the file move.

chatgpt-codex-connector[bot]

This comment was marked as outdated.

Anchor garage coverage on the trailing slash so near-miss inputs
(backend/tasks.py, backend/tasks_extra/**) can't count as watching
backend/tasks/ — Codex and Copilot found this independently; near-miss
rows in the parametrized test pin it.

Watch backend/tasks/** in the scaffold template so a freshly
bootstrapped product passes the garage check. This is what broke the
repo-checks CI job: CI bootstraps spline_reticulator before linting,
which local lint runs never did. The narrowing nag now also names
wiring locations so following its advice doesn't trip the same check.

Treat a missing turbo.json as the broad watch-everything default in the
warehouse_sources vendor guard test instead of crashing on the file the
narrow-or-nothing commit deleted; the vendor enumeration resumes if
narrowing ever returns.

Move the datetime import to module level in error_tracking max_tools —
the inline import traveled along with the file move.
chatgpt-codex-connector[bot]

This comment was marked as outdated.

Follow-up to the slash-anchoring fix: file garages had the symmetric
hole. backend/tasks.py.bak or backend/max_tools.py_extra/** would
startswith-match the real file, so unwatched_garages() certified the
wiring location as watched while changes to the actual file never
re-ran contract-check. Directory locations keep the slash-anchored
prefix; single-file locations now need an exact input. Applied in the
shared coverage helper, so permanent modules and carve-out files get
the same exactness.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

class HogQLQueryFixerTool(MaxTool):
name: str = "fix_hogql_query"
description: str = "Fixes any error in the current HogQL query"
context_prompt_template: str = SQL_ASSISTANT_ROOT_SYSTEM_PROMPT

P1 Badge Gate HogQL fixer before auto-registration

When a client supplies contextual_tools: {"fix_hogql_query": ...} to the conversation API, the registry imports every products.*.backend.max_tools module and this MaxTool subclass is now registered globally; unlike the FixHogQLViewSet path, it still inherits the default empty get_required_resource_access(). That exposes the schema-serializing, billable fixer outside the endpoint's scope_object = "INTERNAL" gate, so add an appropriate access check or keep this class out of the auto-imported max_tools registry.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

"tasks": {
"backend:contract-check": {
"inputs": ["backend/facade/**", "backend/presentation/**", "backend/routes.py"],
"inputs": ["backend/facade/**", "backend/presentation/**", "backend/routes.py", "backend/tasks/**"],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Watch job-log Temporal wiring

When only products/engineering_analytics/backend/logic/job_logs/temporal.py or schedule.py changes, this narrowed input set will not mark backend:contract-check as affected, so turbo-discover can skip Django even though core registers JOB_LOGS_WORKFLOWS, JOB_LOGS_ACTIVITIES, and the schedule through backend/facade/temporal.py. Either move these definitions under a watched wiring location such as backend/temporal/ or include the current backend/logic/job_logs/** implementation path here.

Useful? React with 👍 / 👎.

…wiring-doctrine

Conflict: products/warehouse_sources/turbo.json — deleted here (narrow-or-nothing:
the facade still re-exports 21 non-contract classes, so the product can't hold a
narrowed override), modified on master by #71908 (input negations to cut CI fan-out).
Resolved by keeping the deletion. The IsolationChainCheck gate this branch adds
rejects any narrowing while those leaks exist, and the narrowed inputs from #71908
leave backend/types.py and the facade/testing.py mixin unwatched — real coverage
holes. warehouse_sources earns narrowing back by moving its facade-crossing types
to contracts.py; the #71908 audit maps most of that surface already.
The hosted-platform merge gave stamphog backend/tasks/ and backend/temporal/ garages, but its turbo.json narrowing predates them — the new unwatched-garages gate correctly flags that a change to core-driven implementations there would skip the Django suite.
@webjunkie

Copy link
Copy Markdown
Contributor Author

Merging master hit a conflict with #71908, which re-narrowed products/warehouse_sources/turbo.json while this PR deletes that file. I kept the deletion: the wiring gate added here blocks narrowing while the facade still re-exports 21 non-contract classes, and the #71908 input list leaves backend/types.py and the facade/testing.py mixin unwatched, so a change there would skip the Core tests that exercise them. warehouse_sources runs broad (fail-safe, more CI) until its facade-crossing types move to facade/contracts.py — the audit in #71908 already maps most of that surface, so the earn-back narrowing can bring those exclusions straight back.

Also added stamphog's new backend/tasks/ and backend/temporal/ garages to its contract-check inputs — the new unwatched-garages gate flagged them after the hosted-platform merge.

@webjunkie
webjunkie merged commit d02b44c into master Jul 17, 2026
246 checks passed
@webjunkie
webjunkie deleted the chore/devex-facade-wiring-doctrine branch July 17, 2026 16:21
@deployment-status-posthog

deployment-status-posthog Bot commented Jul 17, 2026

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-07-17 16:59 UTC Run
prod-us ✅ Deployed 2026-07-17 17:19 UTC Run
prod-eu ✅ Deployed 2026-07-17 17:18 UTC Run

posthog Bot added a commit that referenced this pull request Jul 17, 2026
The isolating-product-facade-contracts skill told authors to narrow
backend:contract-check inputs to only backend/facade/** and
backend/presentation/**. Since the wiring-couplings doctrine (#71486),
core-dispatched implementations (query runners, Max tools, Temporal
workflows, celery tasks) and sanctioned model-registration modules must
stay in those inputs so a change to them still re-runs the full suite.
Following the skill verbatim preserved an unsound CI skip.

Update step 6.4 and the done criteria to require the wiring locations
(and backend/routes.py) alongside facade/presentation, and point at the
error_tracking and visual_review turbo.json examples.

Generated-By: PostHog Code
Task-Id: f15e5796-88fe-432e-8301-938605c272ba
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants