Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
UN-3396 [MISC] Address coderabbit round 3 review feedback
Eight new CodeRabbit findings landed on PR #1908 after the
terminology-alignment push. Seven are real; one is a stale re-scan of
an already-fixed item. Fixing the seven in place; the eighth (SKILL.md
fence without language identifier at line 103) is already resolved by
commit 3eda6e7 and needs no action.

Changes:

* backend/account_v2/DESIGN_RULES.md
  - Read first table: principle reference for fail-closed was P8, now
    P5. P8 is "internal vs external surfaces," P5 is "fail closed on
    permission decisions" — R2 is about the second one.
  - R2 Refs: same fix (`principles.md#P8` → `principles.md#P5`).
  - R4 Enforced by: was `not yet enforced — see project issue tracker
    (credential lifecycle)`. The contract requires a concrete tracker
    reference for `not yet enforced`; no real tracker ID exists yet.
    Downgraded to `code review only` (the actual current enforcement
    state) — same move previously applied to workers/shared R3. If a
    real tracker ID ever gets assigned, flip back to the
    `not yet enforced` form with the ID.

* unstract/connectors/DESIGN_RULES.md
  - Read first table: P8 → P5 (same fail-closed fix as account_v2).
  - R4 (Connector errors surface as the framework's exception
    hierarchy): Refs previously pointed at `principles.md#P8`, which
    doesn't fit — R4 is about type-boundary hygiene between driver
    exceptions and the framework exception hierarchy, not about URL
    group / auth middleware separation (which is what P8 actually
    says). No existing principle fits cleanly. Removed the principle
    reference entirely; Refs now points only at `security/standards.md`,
    which is honest about the rule's provenance.

* design-rules/principles.md
  - P1 Implementation row previously named only
    `DefaultOrganizationMixin`, which is only the model mixin (adds
    the FK and auto-populates it on save). The query-time filter is
    provided by `DefaultOrganizationManagerMixin` (a separate manager
    mixin living in the same file — verified in
    backend/utils/models/organization_mixin.py). Updated the row to
    name both classes and explain the split: the model mixin adds the
    FK, the manager mixin filters queries, and together they provide
    auto-scoping for direct-FK models. CodeRabbit's suggestion to
    "reconcile to one name" was based on the incorrect assumption
    that the two names refer to the same class — they don't.

* design-rules/per-component-contract.md
  - Rule format section: softened the overclaim that `validate.sh`
    "checks the field presence" of rule rows. The script does not
    parse rule tables today; it only greps for the Compatibility
    substring, the forbidden-word list, and directory sanity. Reviewers
    are the current enforcement of rule-field presence, with a note
    that a future hardening pass may extend the script.
  - "When to add a per-component file" section: removed the stale
    "`## Known Exceptions` and `## Checklist` sections are still
    required" wording, which contradicted the earlier commit making
    `## Known Exceptions` optional. Now says only `## Checklist` is
    still required, and Known Exceptions is included only when an
    accepted exception exists.
  - Self-update reminder: softened item 2 to describe what
    `validate.sh` actually greps for (Compatibility blockquote
    wording, forbidden-word list) instead of the `## Checklist`
    section header and rule field names (which it does not currently
    grep for).

Not addressed (deliberately):

* SKILL.md line 103 fence language identifier: already fixed in
  commit 3eda6e7 (the fence is `` ```markdown ``, not bare). The
  CodeRabbit finding at 11:22:54Z is a stale re-scan of the
  pre-alignment state.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
  • Loading branch information
muhammad-ali-e and claude committed Apr 8, 2026
commit 66ab7648e6d4c70b5350c53d6db1afac5dc979f7
6 changes: 3 additions & 3 deletions backend/account_v2/DESIGN_RULES.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ This file follows the [per-component contract](../../design-rules/per-component-

| File | Why it binds here |
|---|---|
| [`principles.md`](../../design-rules/principles.md) | P1 (org scoping), P2 (credentials), P8 (fail-closed) |
| [`principles.md`](../../design-rules/principles.md) | P1 (org scoping), P2 (credentials), P5 (fail-closed) |
| [`ai-review-checklist.md`](../../design-rules/ai-review-checklist.md) | 9 questions every change must answer |
| [`security/tenant-isolation.md`](../../design-rules/security/tenant-isolation.md) | Three-Layer Defense — `account_v2` owns Layer 1 (middleware) and is the root of Layer 3 (filter backend) |
| [`adr/ADR-001`](../../design-rules/adr/ADR-001-org-scoping-query-layer.md) | Org scoping is enforced at the query layer, not via RLS |
Expand All @@ -43,7 +43,7 @@ This file follows the [per-component contract](../../design-rules/per-component-
|---|---|
| **Severity** | MUST |
| **Why** | The middleware is Layer 1 of the Three-Layer Defense — it validates the user belongs to the org and stores `org_id` in the thread-local `StateStore`. Reading the org from a header, request param, or cookie directly bypasses validation and is a tenant boundary violation. |
| **Refs** | `principles.md#P8` · `security/tenant-isolation.md` |
| **Refs** | `principles.md#P5` · `security/tenant-isolation.md` |
| **Enforced by** | `CustomAuthMiddleware` + code review |

### R3 — Org membership has exactly one source of truth: `OrganizationMember`
Expand All @@ -62,7 +62,7 @@ This file follows the [per-component contract](../../design-rules/per-component-
| **Severity** | MUST |
| **Why** | Duplicating a credential makes rotation impossible and leaves stale copies behind on member departure. `User`-owned credentials must be referenced by ID from anywhere that consumes them, never copied. |
| **Refs** | `principles.md#P2` |
| **Enforced by** | not yet enforced — see project issue tracker (credential lifecycle) |
| **Enforced by** | code review only |

---

Expand Down
6 changes: 3 additions & 3 deletions design-rules/per-component-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Every rule in the `## Rules` section uses this exact format — an `H3` heading
| **Enforced by** | <test path | middleware | CI check | code review only | not yet enforced — <ref>> |
```

Every rule must have all four rows (Severity, Why, Refs, Enforced by). `validate.sh` checks the field presence, not the field content.
Every rule must have all four rows (Severity, Why, Refs, Enforced by). Rule-field presence and checklist-section presence are checked at code review today — `validate.sh` does not yet parse rule tables; it currently only verifies the Compatibility blockquote substring, the forbidden-word list, and component-directory sanity. A future hardening pass may extend it to enforce the full rule format; until then, reviewers are the enforcement.

The middle dot (`·`) is the canonical separator for multiple `Refs` entries — easier to scan than a comma in a table cell.

Expand Down Expand Up @@ -171,7 +171,7 @@ Why: a rule system that contradicts itself is no rule system at all. Reviewers c

- Whenever a new active Django app is added under `backend/`, a new shared library directory is added under `unstract/`, or a new worker directory is added under `workers/`. M1 above.
- Copy the structure from a real, current file — `backend/account_v2/DESIGN_RULES.md` is the canonical reference.
- If the new component has no specific rules yet, the `## Rules` section contains a single line: `No component-specific rules yet.` The file is still mandatory, and the `## Known Exceptions` and `## Checklist` sections are still required.
- If the new component has no specific rules yet, the `## Rules` section contains a single line: `No component-specific rules yet.` The file is still mandatory; keep `## Checklist`, and include `## Known Exceptions` only when at least one accepted exception exists.

Comment thread
coderabbitai[bot] marked this conversation as resolved.
---

Expand All @@ -189,7 +189,7 @@ Why: a rule system that contradicts itself is no rule system at all. Reviewers c
If you change this contract, expect to:

1. Update [`definition-of-done.md`](definition-of-done.md) if the change affects the checklist body, severity vocabulary, or M1/M2/M3 wording.
2. Update `.claude/skills/design-rules/scripts/validate.sh` if any literal string the script greps for changes (the Compatibility blockquote, the `## Checklist` section header, or the rule field names).
2. Update `.claude/skills/design-rules/scripts/validate.sh` if the Compatibility blockquote wording changes or the forbidden-word list needs adjusting — those are the literal strings the script currently greps for. (The script does not today enforce rule-format or section-header literals; if a future hardening pass adds those checks, list them here.)
3. Update `.claude/skills/design-rules/SKILL.md` if the contract introduces a new section that the `get` or `review` commands must surface.
4. Re-run `validate.sh` against every per-component file in the repo.
5. If the section structure itself changes, the change touches every per-component file. That is intentional: a structural change is a real event and a 41-file diff is the right blast radius.
2 changes: 1 addition & 1 deletion design-rules/principles.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Principles are referenced from per-component files via `principles.md#PN` (e.g.
| | |
|---|---|
| **Rule** | Every model that holds tenant data must be reachable from `Organization` and must be filtered by the requesting user's organization on every read and write. |
| **Implementation** | Use `DefaultOrganizationMixin` for direct FK ownership, and `OrgAwareManager` or `OrganizationFilterBackend` for indirect ownership through a parent FK chain. See [`security/tenant-isolation.md`](security/tenant-isolation.md) for the Three-Layer Defense. |
| **Implementation** | Tenant models with a direct `organization` FK use `DefaultOrganizationMixin` (model mixin — adds the FK and auto-populates it on save) together with `DefaultOrganizationManagerMixin` (manager mixin — filters every queryset by the current org). Models that reach `Organization` only through a parent FK chain use `OrgAwareManager` or rely on `OrganizationFilterBackend` (the view-layer backend that BFS-walks the FK chain). See [`security/tenant-isolation.md`](security/tenant-isolation.md) for the full Three-Layer Defense. |
| **Example** | A new model that stores documents must add a direct `organization` FK or a path that BFS-resolves to one. A new DRF view on a tenant model must inherit the project's filter backend so cross-org reads are impossible. |

---
Expand Down
4 changes: 2 additions & 2 deletions unstract/connectors/DESIGN_RULES.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ This file follows the [per-component contract](../../design-rules/per-component-

| File | Why it binds here |
|---|---|
| [`principles.md`](../../design-rules/principles.md) | P2 (credentials), P8 (fail-closed) |
| [`principles.md`](../../design-rules/principles.md) | P2 (credentials), P5 (fail-closed) |
| [`ai-review-checklist.md`](../../design-rules/ai-review-checklist.md) | 9 questions every change must answer |
| [`security/standards.md`](../../design-rules/security/standards.md) | `EncryptedBinaryField` on `ConnectorInstance.connector_metadata`, SQL Safety Standard (S1) for the databases subtree |

Expand Down Expand Up @@ -60,7 +60,7 @@ This file follows the [per-component contract](../../design-rules/per-component-
|---|---|
| **Severity** | MUST |
| **Why** | Driver-level exceptions (`psycopg2.*`, `google.api_core.*`, `boto3.ClientError`, …) must be wrapped in the `unstract.connectors.exceptions` hierarchy before leaving the connector. Callers depend on a stable error contract to distinguish retryable, auth, and misconfiguration failures — leaking the raw driver exception couples callers to the driver and defeats retry/backoff policy. |
| **Refs** | `principles.md#P8` |
| **Refs** | `security/standards.md` |
| **Enforced by** | code review only |

### R5 — Database connectors inherit the SQL Safety Standard from `databases/`
Expand Down