Apply Rules
Apply Rules map workspace paths to resource sets, automatically activating the right resources based on where agents are working.
What Are Apply Rules?
Apply rules answer the question: “When an agent works in this directory, which resource sets should be available?”
They’re defined in .shai/config.yaml:
1apply:
2 - path: ./
3 resources: [base-allowlist]
4
5 - path: frontend
6 resources: [npm-registries, playwright]
7
8 - path: backend/payments
9 resources: [stripe-api, database-access]Why Apply Rules?
Without apply rules, you’d need to manually specify resource sets every time:
1# Without apply rules (tedious!)
2shai -rw frontend/components --resource-set npm-registries --resource-set playwrightWith apply rules, Shai automatically resolves the right resources:
1# With apply rules (automatic!)
2shai -rw frontend/components
3# Automatically gets: base-allowlist, npm-registries, playwrightHow Rules Work
Path Matching
Rules match workspace-relative paths:
1apply:
2 - path: ./ # Matches everything
3 resources: [base]
4
5 - path: frontend # Matches frontend/**/*
6 resources: [npm]
7
8 - path: backend/auth # Matches backend/auth/**/*
9 resources: [database]When you run:
1shai -rw backend/auth/handlersShai finds all rules that match backend/auth/handlers:
./✅ (matches everything)frontend❌ (doesn’t match)backend/auth✅ (matches)
Result: [base, database]
Aggregation
Resource sets from all matching rules are combined:
1apply:
2 - path: ./
3 resources: [base-allowlist]
4
5 - path: backend
6 resources: [database-access]
7
8 - path: backend/payments
9 resources: [stripe-api, payment-webhooks]Running shai -rw backend/payments:
./matches →[base-allowlist]backendmatches →[database-access]backend/paymentsmatches →[stripe-api, payment-webhooks]
Final result: [base-allowlist, database-access, stripe-api, payment-webhooks]
Evaluation Order
Rules are evaluated top to bottom, but all matching rules are applied:
1apply:
2 - path: ./
3 resources: [base]
4
5 - path: backend
6 resources: [api-tools]
7
8 - path: backend # Duplicate path is fine
9 resources: [testing]All matching backend rules are applied: [base, api-tools, testing]
Image Overrides
Apply rules can also override the container image:
1apply:
2 - path: ./
3 resources: [base-allowlist]
4 # Uses default image from top-level config
5
6 - path: infrastructure
7 resources: [deployment-tools]
8 image: ghcr.io/my-org/devops:latest
9
10 - path: ml-training
11 resources: [gpu-access]
12 image: ghcr.io/my-org/pytorch-gpu:latestWhen you run shai -rw infrastructure/terraform, it uses the devops:latest image instead of the default.
Image Override Rules
More specific paths take precedence:
1apply: 2 - path: ./ 3 image: ghcr.io/colony-2/shai-mega 4 5 - path: backend/payments 6 image: ghcr.io/my-org/payments-dev:latestRunning
shai -rw backend/paymentsusespayments-dev:latest.Root path (
.or./) cannot override the image:1apply: 2 - path: ./ 3 image: foo:latest # ❌ Ignored! Use top-level `image` key insteadCLI flag takes ultimate precedence:
1shai -rw infrastructure --image custom:latest 2# Uses custom:latest regardless of config
Path Syntax
Relative Paths
All paths in apply rules are relative to the workspace root:
1apply:
2 - path: ./ # Root (matches everything)
3 - path: . # Same as ./
4 - path: src # Matches src/**/*
5 - path: src/backend # Matches src/backend/**/*Subdirectory Matching
Paths match themselves and all subdirectories:
1apply:
2 - path: backend
3 resources: [api-tools]This matches:
backend/✅backend/auth/✅backend/payments/handlers/✅frontend/❌
Exact vs Prefix Matching
Currently, Shai uses prefix matching. There’s no way to match a path exactly without matching its subdirectories.
To work around this, use more specific paths:
1apply:
2 - path: backend
3 resources: [common-backend-tools]
4
5 - path: backend/payments
6 resources: [payment-specific-tools]Multiple Target Paths
When you specify multiple -rw paths, Shai resolves resources for each path and aggregates:
1shai -rw frontend/app -rw backend/api1apply:
2 - path: ./
3 resources: [base]
4
5 - path: frontend
6 resources: [npm]
7
8 - path: backend
9 resources: [database]Resolution:
frontend/appmatches:[base, npm]backend/apimatches:[base, database]- Aggregate (deduplicate):
[base, npm, database]
Common Patterns
Layered Resources
Build up resources from general to specific:
1apply:
2 # Everything gets base allowlist
3 - path: ./
4 resources: [base-allowlist]
5
6 # All frontend code gets npm + playwright
7 - path: frontend
8 resources: [npm-registries, playwright]
9
10 # Specific frontend app gets additional CDN access
11 - path: frontend/marketing-site
12 resources: [cdn-access]Per-Service Resources
In a microservices repo:
1apply:
2 - path: ./
3 resources: [base-allowlist]
4
5 - path: services/auth
6 resources: [database-access, jwt-tools]
7
8 - path: services/payments
9 resources: [database-access, stripe-api, payment-testing]
10
11 - path: services/notifications
12 resources: [smtp-access, push-notification-api]Per-Environment Resources
Different paths for different environments:
1apply:
2 - path: ./
3 resources: [base-allowlist]
4
5 - path: infrastructure/staging
6 resources: [staging-credentials, staging-k8s]
7
8 - path: infrastructure/production
9 resources: [production-credentials, production-k8s]Debugging Apply Rules
Use --verbose to see which rules match:
1shai -rw backend/payments --verboseOutput shows:
- Matched rules
- Resolved resource sets
- Final aggregated resources
- Which image is selected
Best Practices
✅ Do
- Start with a root
./rule for common resources - Layer more specific rules on top
- Use descriptive path names that match your project structure
- Keep rules simple and predictable
- Document complex rule hierarchies
❌ Don’t
- Create conflicting rules that make resolution unclear
- Over-specify rules for every single directory
- Use image overrides on the root path
- Assume rules are evaluated in a specific order (always think aggregation)
Examples
Monorepo with Multiple Teams
1apply:
2 # Base for everyone
3 - path: ./
4 resources: [base-allowlist, git-ssh]
5
6 # Web team
7 - path: packages/web-app
8 resources: [npm-registries, playwright]
9
10 - path: packages/mobile-app
11 resources: [npm-registries, react-native-tools]
12
13 # Backend team
14 - path: services/api
15 resources: [database-access, api-testing]
16
17 - path: services/workers
18 resources: [redis-access, queue-monitoring]
19
20 # DevOps team
21 - path: infrastructure
22 resources: [cloud-apis, k8s-access]
23 image: ghcr.io/my-org/devops:latestML Project
1apply:
2 # Base
3 - path: ./
4 resources: [base-allowlist]
5
6 # Data preparation
7 - path: data
8 resources: [s3-access, data-processing]
9
10 # Model training
11 - path: models
12 resources: [gpu-access, wandb-api, model-registry]
13 image: ghcr.io/my-org/pytorch-gpu:latest
14
15 # Serving
16 - path: serving
17 resources: [model-registry, k8s-access]Full-Stack Application
1apply:
2 # Shared base
3 - path: ./
4 resources: [base-allowlist]
5
6 # Frontend
7 - path: frontend
8 resources: [npm-registries, cdn-access]
9
10 # Backend API
11 - path: backend/api
12 resources: [database-access, redis-access]
13
14 # Backend workers
15 - path: backend/workers
16 resources: [redis-access, smtp-access]
17
18 # Database migrations
19 - path: backend/migrations
20 resources: [database-admin-access]
21
22 # Infrastructure
23 - path: infrastructure
24 resources: [terraform-cloud, aws-apis]Next Steps
- Learn about Resource Sets to define collections of resources
- Understand Cellular Development to make the most of apply rules
- See Configuration Reference for complete schema details