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 playwright

With apply rules, Shai automatically resolves the right resources:

1# With apply rules (automatic!)
2shai -rw frontend/components
3# Automatically gets: base-allowlist, npm-registries, playwright

How 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/handlers

Shai finds all rules that match backend/auth/handlers:

  1. ./ ✅ (matches everything)
  2. frontend ❌ (doesn’t match)
  3. 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]
  • backend matches → [database-access]
  • backend/payments matches → [stripe-api, payment-webhooks]

Final result: [base-allowlist, database-access, stripe-api, payment-webhooks]

Resource sets are deduplicated. If multiple rules specify the same set, it’s only included once.

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:latest

When you run shai -rw infrastructure/terraform, it uses the devops:latest image instead of the default.

Image Override Rules

  1. 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:latest

    Running shai -rw backend/payments uses payments-dev:latest.

  2. Root path (. or ./) cannot override the image:

    1apply:
    2  - path: ./
    3    image: foo:latest    # ❌ Ignored! Use top-level `image` key instead
  3. CLI 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/api
1apply:
2  - path: ./
3    resources: [base]
4
5  - path: frontend
6    resources: [npm]
7
8  - path: backend
9    resources: [database]

Resolution:

  1. frontend/app matches: [base, npm]
  2. backend/api matches: [base, database]
  3. 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 --verbose

Output 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:latest

ML 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