UN-3636 [MISC] Merge overlay manifests via UNSTRACT_RIG_EXTRA_MANIFESTS#2188
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughManifest loading now supports environment-specified overlays, tiered selection limits implicit dependency expansion, organization lookup skips database access without an identifier, and LLM mocking no longer depends on ChangesRig manifest and selection
Organization lookup guard
LLM mock activation
Estimated code review effort: 3 (Moderate) | ~30 minutes Sequence Diagram(s)sequenceDiagram
participant CIEnvironment
participant resolve
participant Manifest
CIEnvironment->>resolve: provide selected groups and tier
resolve->>Manifest: expand selected dependencies
Manifest-->>resolve: return expanded groups
resolve->>resolve: filter implicit groups by tier
resolve-->>CIEnvironment: return runnable groups
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
load_groups() now merges extra group manifests listed in the env var (os.pathsep-separated, REPO_ROOT-relative) onto the base tests/groups.yaml before validation, so cross-manifest depends_on and the platform-gate invariant are checked over the union. Name collisions are an error. Lets a downstream repo (the cloud build) contribute its own test groups by copying a groups.cloud.yaml into the merged tree, without editing the OSS manifest. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz
fa7eaf8 to
16f31ce
Compare
|
| Filename | Overview |
|---|---|
| tests/rig/groups.py | Adds overlay manifest loading while keeping explicit manifest loads isolated. |
| tests/rig/tests/test_groups.py | Adds tests for overlay merge behavior, collisions, defaults, bad paths, and explicit-path isolation. |
| tests/rig/selection.py | Filters dependency-expanded groups outside the selected tier while preserving explicit requests. |
| unstract/sdk1/src/unstract/sdk1/llm.py | Removes the ENVIRONMENT check from mock-response injection. |
| backend/utils/user_context.py | Returns None without querying the database when no organization id is present. |
Reviews (10): Last reviewed commit: "UN-3636 [FIX] Stop ide_prompt_complete t..." | Re-trigger Greptile
ade772e to
16f31ce
Compare
UserContext.get_organization() ran Organization.objects.get() even with no org id in StateStore (import time, or management commands with no request), catching only DoesNotExist/ProgrammingError — a DB-less/unmigrated setup hit an uncaught OperationalError. Short-circuit when there's no org id: no query, so serializers/managers that reference org-scoped querysets at class-def can be imported during DB-free test collection.
jaseemjaskp
left a comment
There was a problem hiding this comment.
Automated PR review (PR Review Toolkit: code-reviewer, silent-failure-hunter, type-design-analyzer, pr-test-analyzer, comment-analyzer, code-simplifier).
Clean, well-tested change. The user_context short-circuit is behavior-preserving (all 42 callers already treat None as "no org") and adds no new silent failures; the overlay-manifest merge fails loud on every error path and rejects name collisions rather than silently overriding. No new types introduced. Approving — all findings below are non-blocking nits.
One pre-existing item worth a follow-up (not from this diff): get_organization catches a broad django.db.utils.ProgrammingError and returns None with no logging, which also swallows genuine runtime schema/permission errors under runserver — consider narrowing or logging.
Note: I did not repost @greptile-apps' P1 at groups.py:139 (overlay loop applies to any custom load_groups(path) caller) — it stands and is worth resolving; whatever fix lands there will need the two new rig tests updated, since they rely on overlays applying with an explicit path.
|
Thanks for the thorough review. Taking the P1 at The two new rig tests will need updating since they currently call def test_extra_manifest_merged(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
base = _base_manifest(tmp_path)
overlay = tmp_path / "groups.cloud.yaml"
overlay.write_text(
"""
version: 1
groups:
unit-cloud:
tier: unit
paths: [y]
depends_on: [unit-base]
optional: true
"""
)
monkeypatch.setattr("tests.rig.groups.DEFAULT_MANIFEST", base)
monkeypatch.setenv("UNSTRACT_RIG_EXTRA_MANIFESTS", str(overlay))
manifest = load_groups() # path=None → uses patched DEFAULT_MANIFEST
assert "unit-cloud" in manifest.names()
assert "unit-base" in manifest.expand(["unit-cloud"])
def test_extra_manifest_name_collision_rejected(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
base = _base_manifest(tmp_path)
overlay = tmp_path / "groups.cloud.yaml"
overlay.write_text(
"""
version: 1
groups:
unit-base:
tier: unit
paths: [y]
optional: true
"""
)
monkeypatch.setattr("tests.rig.groups.DEFAULT_MANIFEST", base)
monkeypatch.setenv("UNSTRACT_RIG_EXTRA_MANIFESTS", str(overlay))
with pytest.raises(ValueError, match="already defined"):
load_groups()This also makes the test semantics cleaner — it now exercises the real entry point ( On the pre-existing |
Address review feedback on the UNSTRACT_RIG_EXTRA_MANIFESTS overlay: - Overlays now apply only when loading the default manifest, so an explicit `load_groups(path)` (test fixture, ad-hoc manifest) can no longer absorb a downstream repo's ambient overlay. - `_merge_manifest` returns the merged defaults so an overlay can rename `platform_gate_group` instead of having it silently ignored. - A bad path in the env var raises a ValueError naming the variable rather than a bare FileNotFoundError/IsADirectoryError. - Extract `_load_manifest_dict` to single-source manifest parsing and its error message. - Tests: drive the real default-manifest path; cover overlay isolation, overlay defaults, malformed overlay, and a missing overlay path. - Pin the truthy branch of UserContext.get_organization so inverting the guard can't pass unnoticed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz
There was a problem hiding this comment.
🧹 Nitpick comments (2)
tests/rig/tests/test_groups.py (2)
306-325: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winTest explicit-manifest isolation with an invalid overlay path.
The valid overlay only proves that
unit-cloudis not merged. SetUNSTRACT_RIG_EXTRA_MANIFESTSto a nonexistent path and assertload_groups(base)still succeeds; otherwise a regression could resolve overlays before honoring explicit-manifest isolation.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/rig/tests/test_groups.py` around lines 306 - 325, Update test_explicit_manifest_ignores_overlays to set UNSTRACT_RIG_EXTRA_MANIFESTS to a nonexistent overlay path instead of creating a valid overlay file. Assert load_groups(base) succeeds and still excludes unit-cloud, verifying explicit-manifest isolation occurs before overlay resolution.
251-265: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winCover multiple and
REPO_ROOT-relative overlays.This helper always registers one absolute path, so
os.pathsepsplitting and relative-path resolution remain untested. Add a case with two overlay entries, including one relative toREPO_ROOT, and assert both groups are merged.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/rig/tests/test_groups.py` around lines 251 - 265, Add a test covering multiple overlays in the group-loading tests: create two overlay manifests, register their paths in UNSTRACT_RIG_EXTRA_MANIFESTS joined with os.pathsep, make one path relative to REPO_ROOT, and assert groups from both overlays are merged. Reuse _overlay_env or adjust the test setup as needed while preserving the existing default-manifest behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@tests/rig/tests/test_groups.py`:
- Around line 306-325: Update test_explicit_manifest_ignores_overlays to set
UNSTRACT_RIG_EXTRA_MANIFESTS to a nonexistent overlay path instead of creating a
valid overlay file. Assert load_groups(base) succeeds and still excludes
unit-cloud, verifying explicit-manifest isolation occurs before overlay
resolution.
- Around line 251-265: Add a test covering multiple overlays in the
group-loading tests: create two overlay manifests, register their paths in
UNSTRACT_RIG_EXTRA_MANIFESTS joined with os.pathsep, make one path relative to
REPO_ROOT, and assert groups from both overlays are merged. Reuse _overlay_env
or adjust the test setup as needed while preserving the existing
default-manifest behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4f05d44e-36ff-4f21-bd36-a023b3de2ec0
📒 Files selected for processing (4)
backend/utils/tests/test_user_context.pybackend/utils/user_context.pytests/rig/groups.pytests/rig/tests/test_groups.py
🚧 Files skipped from review as they are similar to previous changes (2)
- backend/utils/tests/test_user_context.py
- tests/rig/groups.py
`--tier X` expanded each selected group's `depends_on` transitively, with no tier bound. `integration-workflow-execution` and `e2e-smoke` both declare `depends_on: [unit-sdk1, unit-workers]`, so those two unit groups ran again in the integration leg and a third time in the e2e leg. Tiers run as separate CI legs and the unit leg already covers them, so dep expansion is now bounded to the requested tier. Explicitly named groups are never dropped, and intra-tier deps (e2e-smoke -> e2e-login) still expand and order as before. Unrun deps do not weaken gating: `blocked_by` intersects with groups that failed in the same run. Measured on the last main run: ~88s of unit-workers and ~48s of unit-sdk1 re-executed per run. On the cloud CI runner the same duplication costs ~350s. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz
* UN-3636 [FIX] Drop the ENVIRONMENT gate on the LLM mock It did not defend the case it was added for. The threat was a worker env block copied out of the test overlay into a real deployment, but the gate was written into that same block, so a copy carries it. Base compose also sets ENVIRONMENT=development on both workers that run the injection, so any deployment derived from it satisfied the gate regardless. That left one real case -- the mock var set alone somewhere that sets no ENVIRONMENT at all -- which holds by accident rather than design, in exchange for depending on a variable nothing else in the codebase reads. What actually guards the hatch is unchanged: it is off unless someone sets UNSTRACT_LLM_MOCK_RESPONSE, and it warns once per process while active. Making mocked spend distinguishable downstream is the defence worth having, and it belongs on the usage record rather than in a config check. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * UN-3636 [MISC] Drop the unread ENVIRONMENT variable from compose Nothing reads it: no service, worker, frontend or plugin looks the variable up, and the one consumer it ever had — the LLM mock gate — was removed in the previous commit. Dropping it everywhere keeps a dead knob from looking load-bearing to the next reader. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ryg9chVDJQggCybpq3YoY3 --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Drop lines that restate the code, trim session-specific detail, and merge comments that duplicated each other across a module and its test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz
`load_groups(DEFAULT_MANIFEST)` merged overlays even though the caller named a manifest explicitly, because the check compared path values. Path equality is also spelling-sensitive, so the same file relative and absolute behaved differently. Key on `path is None` instead: an explicit path loads exactly what it names. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ryg9chVDJQggCybpq3YoY3
TestIdePromptComplete drives the full success path, which reaches
client_plugin_registry.get_client_plugin("subscription_usage"). In OSS no
such plugin is installed, so the lookup returns None instantly. In a tree
with the cloud plugins copied in, it resolves to a real plugin that POSTs
to the backend; with no backend running the call only fails after a
multi-second connect timeout, and _track_subscription_usage swallows the
error so the tests still pass. That accounted for ~190s of the cloud
unit-workers run.
The file already declared _PATCH_GET_PLUGIN but never applied it outside
the dedicated subscription-usage classes. Apply it as a class-scoped
fixture so the lookup is pinned to the OSS answer.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz
|
Unstract test resultsPer-group results
Critical paths
|



What
Three rig changes, bundled because they all land on the same test-infrastructure surface:
load_groups()merges extra group manifests listed in theUNSTRACT_RIG_EXTRA_MANIFESTSenv var (os.pathsep-separated, REPO_ROOT-relative) onto the basetests/groups.yamlbefore validation — so cross-manifestdepends_onand the platform-gate invariant are checked over the union. Group-name collisions are an error, not a silent override.--tier Xnow boundsdepends_onexpansion to tier X.ENVIRONMENTgate on the LLM mock is gone (merged in via UN-3636 [MISC] Drop the ENVIRONMENT gate on the LLM mock #2191), along with the now-unreadENVIRONMENTvariable itself.Why
1. Overlay manifests
Lets a downstream repo contribute its own test groups without editing the OSS manifest. First consumer is the cloud build: it copies a
tests/groups.cloud.yamlinto the merged tree (viacopy_cloud_deps.py) and points the env var at it, so cloud-only plugin test groups (workers, etc.) join the rig without forkinggroups.yaml. No cloud specifics leak into OSS.2. Cross-tier deps
integration-workflow-executionande2e-smokeboth declaredepends_on: [unit-sdk1, unit-workers]. Because--tierexpanded deps transitively with no tier bound, those two unit groups ran three times per CI run — once in the unit leg, again in integration, again in e2e. Measured on run29891886572(main):unit-sdk1unit-workers≈136s of duplicate execution per run. On the cloud CI runner (
custom-k8s-runner, 2 xdist workers) the same duplication costs ~350s.3. Dropping the
ENVIRONMENTgate (#2191)Follow-up to #2170, removing a guard that PR added. The gate required
ENVIRONMENT ∈ {test, development}on top ofUNSTRACT_LLM_MOCK_RESPONSEbefore the SDK would mock a completion. It was added for this review comment — the hazard being a worker env block copied out of the test overlay into a real deployment, where litellm's synthetic 10/20/30 token usage would flow into the usage tables indistinguishable from real spend.It did not defend that case:
docker/docker-compose.yamlsetENVIRONMENT=developmenton both workers that run the injection, so any deployment derived from base compose — which is how Unstract is self-hosted — satisfied the gate anyway.That left one case it genuinely caught: the mock var set alone somewhere that sets no
ENVIRONMENTat all. That held by accident (nothing sets the variable in k8s) rather than by design, and it cost a dependency on a variable nothing else in the codebase reads — so the variable was dropped from compose too, rather than left looking load-bearing.What still guards the hatch is unchanged: it is off unless someone sets
UNSTRACT_LLM_MOCK_RESPONSE, and it logs a warning once per process while active. The defence worth having is making mocked spend distinguishable downstream — anis_mockmarker on the usage record, which config checks can't be copy-pasted around. Not in this PR.Changes
Overlays
tests/rig/groups.py:_extra_manifest_paths(),_load_manifest_dict(),_merge_manifest(), wired intoload_groups().tests/rig/tests/test_groups.py: +6 tests (merge with cross-manifestdepends_on, name collision, explicit-manifest isolation, overlay defaults, malformed overlay, missing overlay path).Cross-tier deps
tests/rig/selection.py: with--tier, drop dep-expanded groups outside that tier.tests/rig/tests/test_selection.py: +3 tests (cross-tier dep not pulled in, explicit request survives the filter, expansion unchanged without a tier filter).LLM mock gate (#2191)
unstract/sdk1/src/unstract/sdk1/llm.py: drop_ENVIRONMENT_ENV,_MOCK_ALLOWED_ENVIRONMENTS,_warn_mock_refused, and the gate.unstract/sdk1/tests/test_mock_response.py: drop the 4 gate tests and the autouse env fixture (9 tests remain, all passing).tests/compose/docker-compose.test.yaml: drop the twoENVIRONMENT=testlines added for the gate.docker/docker-compose.yaml: drop the unreadENVIRONMENTdeclarations.tests/README.md: record why it was removed, so it doesn't get re-added.No-org short-circuit
backend/utils/user_context.py:get_organization()returnsNonewhen no org is in context (import time, management commands with no request) instead of querying, so evaluating it on a DB-less/unmigrated setup can't fail — which is what the rig's backend groups do at collection time.backend/utils/tests/test_user_context.py: cover both branches, including the truthy one so inverting the guard can't pass unnoticed.Safety
load_groups(path)stays isolated.e2e-smoke→e2e-login); explicitly named groups are never dropped by the tier filter.blocked_byintersects with groups that failed in the same run, so a dep that wasn't run simply doesn't gate — its own tier leg gates it.UNSTRACT_LLM_MOCK_RESPONSEand warns once per process while active.tests.rig validateOK — 18 groups, 16 critical paths. Full rig suite: 80 passed, 1 skipped.🤖 Generated with Claude Code
https://claude.ai/code/session_01RnLaN45ShBbcCXWZkqCThz