# Environment Reconciliation Analysis (repository-based)

> Read-only analysis. No implementation. The repository is the source of truth.

## Summary

The Managed Agents code uses **two independent environment vocabularies** for the
`environment` concept:

| Set | Values | Default | Used by |
|---|---|---|---|
| **A — Knowledge/Runtime** | `dev`, `staging`, `production` | `production` | Knowledge Cache, Loader, Agent Runtime, admin knowledge card |
| **B — Pipeline** | `dev`, `test`, `prod` | `prod` | Agents registry, Action Policies, Decision endpoint, Testing/Actions admin |

They overlap only on the token `dev`. `staging` and `production` exist **only** in
Set A; `test` and `prod` exist **only** in Set B. No code maps one set to the
other — a single end-to-end flow legitimately spans both (runtime `environment=production`
loads knowledge; the follow-on decision uses `environment=prod` for policy).

> **Scope note:** the `staging` strings in `config/sms.php`, `config/notifications.php`,
> `config/investor_workflow.php`, `SecureStartupFileStore.php`, `InvestorSignupController.php`
> refer to the **Laravel app environment / feature toggles**, not the managed-agents
> `environment` field. They are out of scope for this reconciliation.

---

## Occurrence inventory

### Set A — `dev / staging / production` (Knowledge / Loader / Runtime)

| File | Location | Purpose | Runtime-critical | Migration risk |
|---|---|---|---|---|
| `config/agent_knowledge.php` | `:28-29` | `environments` list + `default_environment='production'` (env `AGENT_KNOWLEDGE_DEFAULT_ENV`) | **Yes** | Config-only (low) |
| `database/migrations/2026_07_02_140000_add_environment_to_agent_knowledge_cache.php` | `:10` + column | Adds `agent_knowledge_cache.environment` (comment `dev\|staging\|production`) | **Yes** | **DATA** (existing rows hold `production`) |
| `App\Models\AgentKnowledgeCache` | table col | Per-`(agent_key, environment)` version sequence + active pointer; `unique(agent_key, environment, version)` | **Yes** | **DATA** + unique-index consideration |
| `App\Services\Agents\Knowledge\AgentKnowledgeReader` | `env()` `:20` | Default env for reads | **Yes** | Code |
| `App\Services\Agents\Knowledge\AgentKnowledgeLoader` | `env()` `:205-206` | Validate + default env for refresh/rollback | **Yes** | Code |
| `App\Services\Agents\Runtime\RuntimeRunner` | `environment()` `:220-221` | Validate + default env for the runtime | **Yes** | Code |
| `App\Http\Controllers\Api\V1\Ai\Agents\AgentKnowledgeController` | `env()` `:107` | Default env for knowledge reads | **Yes** | Code |
| `App\Http\Controllers\Admin\Agents\AgentConsoleController` | `:132,135,183,187` | Admin knowledge card env list/default | No (admin UI) | Code |
| `tests/Feature/Agents/AgentRuntimeRunTest.php`, `AgentKnowledgeCacheTest.php` | `:47`, `:63` | Test config | N/A | Tests |

### Set B — `dev / test / prod` (Agents / Policies / Decision)

| File | Location | Purpose | Runtime-critical | Migration risk |
|---|---|---|---|---|
| `config/agents.php` | `:17` | `default_environment='prod'` (env `AI_AGENT_ENV`) | **Yes** | Config-only |
| `App\Models\Agent` | `ENV_PROD='prod'` `:20` | Agent env constant | **Yes** | Code |
| `database/seeders/AgentsSeeder.php` | `ENV='prod'` `:23` | Seeds `agents.environment='prod'` | **Yes** | Seed + **DATA** |
| `database/migrations/2026_06_26_110001_create_agents_table.php` | `:24` | `agents.environment` `string(8) default 'prod'` (comment `dev\|test\|prod`) | **Yes** | Column + **DATA** |
| `database/migrations/2026_06_26_110005_create_agent_action_policies_table.php` | `:24` | `agent_action_policies.environment` `string(8) default 'prod'` | **Yes** | Column + **DATA** |
| `App\Services\Agents\AgentDecisionService` | `:49` | Decision env default `'prod'` (policy resolution scope) | **Yes (core)** | Code |
| `App\Http\Controllers\Api\V1\Ai\Agents\AgentRunController` | `:30` | Decision endpoint validation `in:dev,test,prod` | **Yes (core)** | Code |
| `App\Http\Controllers\Api\V1\Ai\Agents\AgentRegistryController` | `:57,74` | Agent register/update `in:dev,test,prod` | **Yes** | Code |
| `App\Http\Controllers\Api\V1\Ai\Agents\ActionPolicyController` | `:34,47,65` | Policy default `'prod'`, `in:dev,test,prod` | **Yes** | Code |
| `App\Http\Controllers\Api\V1\Ai\Agents\TestingController` | `:86,113,120` | Testing scenarios `in:dev,test,prod`, default `prod` | No (test surface) | Code |
| `App\Http\Controllers\Admin\Agents\AgentConsoleController` | `:74,91` | Agent store `in:dev,test,prod`; `$env='prod'` | No (admin UI) | Code |
| `App\Http\Controllers\Admin\Agents\ActionsController` | `:35,73,126` | `$env='prod'`; `in:dev,test,prod` | No (admin UI) | Code |

### Cross-namespace touch point
`RuntimeRunner` (Set A, `production`) produces a proposal that Make submits to the
decision endpoint (Set B, `prod`). The two are passed as **independent** parameters
by the caller; no code translates between them.

---

## Runtime-criticality + fail-safe assessment

The mismatch is **fail-safe**, not silently wrong:

- The **decision endpoint validates `in:dev,test,prod`** (`AgentRunController:30`). Sending `production` there → **422** (rejected), never a mis-scoped action.
- The **runtime/knowledge layer** looks up an active package by exact string. Sending `prod` there (no such package) → **409 `knowledge_unavailable`** / no active row — a hard stop, never a wrong package.
- Therefore a value mixup **fails closed** on both sides. There is no path where the wrong environment silently executes.

This is why the split is a **clarity / maintainability** issue, not a correctness defect.

---

## Recommended canonical environment set

**Canonical: `dev / test / prod`** (adopt Set B).

Rationale (repository-grounded):
- Set B is the **system of record** for the decision/policy pipeline — it has seeded
  DB rows (`AgentsSeeder`), model constants (`Agent::ENV_PROD`), two table columns
  with defaults, and the core `AgentDecisionService`/`ActionPolicyService` scope.
- Set A (`production`) is newer and confined to the Knowledge/Runtime layer (one
  table column + config + a handful of service defaults).
- Migrating the **smaller, newer** surface (`production → prod`) is lower-risk than
  renaming seeded production policy/agent data and the entire decision pipeline.
- `staging`: appears **only** in Set A config/tests — no `staging` DB data, no policy
  counterpart. Recommendation: treat `staging` as an **optional knowledge-only**
  environment (knowledge packages can exist for staging even if it has no seeded
  policies), or retire it if a staging deployment is never provisioned (`NOT VERIFIED
  IN REPOSITORY` that staging exists).

Net canonical: **`dev`, `test`, `prod`** for policy/decision/agents/knowledge/runtime,
with optional `staging` retained only in the knowledge config if staging deployments
are confirmed.

---

## Migration sequence (proposed — NOT to be implemented yet)

1. **Dual-accept (backward-compatible read):** in the Knowledge/Runtime env resolver,
   accept both `production` and `prod` as equivalent (map `production → prod` on read).
   No data change; unblocks everything.
2. **Data migration:** `UPDATE agent_knowledge_cache SET environment='prod' WHERE environment='production'`
   (and decide `staging`). Safe against `unique(agent_key, environment, version)` because
   no `prod` knowledge rows exist today (knowledge uses `production`).
3. **Config flip:** `config/agent_knowledge.php` → `default_environment='prod'`,
   `environments=['dev','test','prod']` (+`staging` if kept). Update the ~6 service/
   controller default fallbacks (`'production' → 'prod'`).
4. **Remove dual-accept** once all rows/callers are on `prod`.
5. **Docs/tests:** update `knowledge-api-contract.md`, `AgentRuntimeRunTest`,
   `AgentKnowledgeCacheTest`, and `BuildsAgentSchema` env strings.

Affected files (exhaustive, for planning): `config/agent_knowledge.php`;
`AgentKnowledgeReader`, `AgentKnowledgeLoader`, `RuntimeRunner`, `AgentKnowledgeController`,
`AgentConsoleController`; migration `2026_07_02_140000` (comment only) + a new data
migration; tests `AgentRuntimeRunTest`, `AgentKnowledgeCacheTest`, `BuildsAgentSchema`.
(Set B files need **no** change under this recommendation.)

## Backward-compatibility strategy

- The **dual-accept** window (step 1) means in-flight callers using `production`
  continue to work while data migrates.
- The decision endpoint already rejects `production` (`in:dev,test,prod`), so **no**
  transition shim is needed there — only the knowledge/runtime layer needs dual-accept.
- Historical `agent_knowledge_cache` rows remain reproducible: the data migration only
  renames the `environment` label; `version`, `checksum`, and `files_json` are unchanged,
  and any AgentRun's `knowledge_checksum` still resolves cross-environment via
  `AgentKnowledgeReader::byChecksum()`.

## Before or after Make integration?

**After (or in parallel) — it does NOT block Make integration.** Because the mismatch
is fail-safe (422/409 on a wrong value, never a silent cross-env action) and Make passes
runtime-env and decision-env as separate parameters, Make can integrate against the
current split immediately. Recommended immediate step: **document the `production ↔ prod`
mapping** (this file) so integrators aren't surprised; schedule the data migration as a
**post-integration hardening** task once the runtime→decision happy path is proven with Make.
