# CIAgent Grill Report

## Run: 2026-08-12 (mode: self-grill, focus: all axes) — v1.24 Consumer Guide Accuracy & Env-Promotion Lifecycle Enforcement

### Overall Verdict: PROCEED  (confidence: 0.82)

The plan is sound. The user directive is clear and binding. The code
integration points are confirmed by inspection. Two binding revisions
applied (both low-risk doc clarifications). No escalations.

---

## Axis 1: Feasibility

**Challenge:** Can `run_platform.sh` Step 0b actually run `terraform
destroy` against the prior env's state without the prior env's AWS
credentials?

**Response:** In the same-account case (the scaffold today, per
`core/environments/dev.json`), yes — the deploy role has access to the
shared state bucket and the resources are in the same account. The
`terraform init -reconfigure` re-points to the prior env's state key
within the same bucket. Cross-account is explicitly out of scope
(D-205). **Confidence: 0.85.**

**Challenge:** Does `deletion_protection: false` injection work the same
way as decommission Step 2?

**Response:** Yes. `scripts/run_decommission.sh:34-37` sets
`res['nfrs']['deletion_protection'] = False` on every resource. The
contract resolver propagates `inputs.deletion_protection` to children's
NFRs (`core/contract_resolver.py:360-372`). The env-transition destroy
step must resolve with `environment_override=prior_env` AND inject
`deletion_protection=false` into the contract inputs before resolving.
This is a confirmed pattern. **Confidence: 0.90.**

**Verdict:** FEASIBLE.

## Axis 2: Scope

**Challenge:** Is 4 phases (P1-P3 + P4) the right size, or is this
over-scoped?

**Response:** 15 requirements across 3 execution phases is
well-scoped. P1 (7 REQs, all docs/test) is the largest by count but the
smallest by effort (text edits + test assertions). P2 (6 REQs, feat) is
the core implementation. P3 (2 REQs, test) is coverage. P4 is final
review. This is a tight, coherent milestone. **Confidence: 0.88.**

**Challenge:** Should the cross-account destroy be in scope?

**Response:** No. The scaffold is single-account. Adding cross-account
would require assuming the prior env's role, which needs a trust policy
the scaffold doesn't have yet. Deferring is pragmatic. The Out of Scope
section documents this. **Confidence: 0.85.**

**Verdict:** PROPERLY-SCOPED.

## Axis 3: Cost / ROI

**Challenge:** Is the env-transition feature worth the complexity?

**Response:** Yes. The user identified a real orphaned-resources risk
that violates the platform's full-lifecycle-management mission. The
fix is a ~80-line Python module + a shell block. The alternative
(blocking env edits, forcing Shape B) contradicts the user's directive.
The ROI is high: closes a real lifecycle gap with minimal code.
**Confidence: 0.90.**

**Verdict:** JUSTIFIED.

## Axis 4: Correctness

**Challenge:** The `detect_prior_env` query — is querying by
`contractId#submittedAt` SK prefix correct for finding the last-applied
env?

**Response:** The `nova-contracts` table has PK `consumerRepo` and SK
`contractId#submittedAt`. To find the last record for a given
contractId, we query by PK `consumerRepo` + SK `begins_with
"contractId#"` + FilterExpression `status = "submitted"` (or
`#LAST_APPLIED`), sort by `submittedAt` desc, take the first. This is
correct DynamoDB pattern. The `record_applied_env` step writes a new
item with SK `contractId#LAST_APPLIED#<timestamp>` so the detect step
can filter by `begins_with "contractId#LAST_APPLIED#"`. **Confidence:
0.85.**

**Challenge:** What if the DynamoDB table doesn't exist in local/CI
mode?

**Response:** The detect step catches `ClientError` / `EndpointNotFound`,
logs a warning, and returns `None` (no prior env). The pipeline proceeds
normally. This is the conservative path — no false-positive destroys.
**Confidence: 0.90.**

**Verdict:** CORRECT.

## Axis 5: Testing

**Challenge:** Can the env-transition behavior be tested without live
AWS?

**Response:** Yes. `test_env_transition.py` uses moto for DynamoDB
(mock_aws pattern from `test_contract_ingestor.py:74-110`).
`test_run_platform_env_transition.py` uses shell-text assertions
(pattern from `test_pipeline.py:79-95`). No live AWS needed.
**Confidence: 0.92.**

**Verdict:** TESTABLE.

## Axis 6: Security

**Challenge:** Does the destroy step introduce a risk of destroying the
wrong resources?

**Response:** The destroy targets the prior env's state key
(`spike/{id}/{prior_env}/terraform.tfstate`). The state key is
deterministic and env-scoped. The destroy can only affect resources in
that state file. The `deletion_protection=false` injection is scoped to
the destroy step only — the new env's apply runs with the default
`deletion_protection=true`. **Confidence: 0.88.**

**Challenge:** Could a malicious consumer trigger a destroy of another
consumer's resources?

**Response:** No. The DynamoDB query is scoped by PK `consumerRepo`
(the consumer's own repo identity). The destroy runs under the
consumer's ABAC-scoped deploy role, which can only touch resources
tagged `nova:owner=<consumer-repo>`. A consumer cannot query or destroy
another consumer's stack. **Confidence: 0.90.**

**Verdict:** SECURE.

## Axis 7: Maintainability

**Challenge:** Is the `core/env_transition.py` module a clean
abstraction or a one-off?

**Response:** It's a reusable module with two functions
(`detect_prior_env`, `record_applied_env`) that encapsulate the
DynamoDB query logic. It can be extended for cross-account destroy in a
future milestone. The shell Step 0b is a thin orchestrator. This is
maintainable. **Confidence: 0.85.**

**Verdict:** MAINTAINABLE.

## Axis 8: Docs consistency

**Challenge:** Will the consumer guide be internally consistent after
P1?

**Response:** The 5 fixes address all known inconsistencies: field
table ↔ schema, Step 2 ↔ Step 4, Step 5 ↔ environments doc, Step 8 ↔
per-env section, reference table ↔ sample contracts. The test updates
assert both shapes are documented. A manual end-to-end read in P1's
verification step catches any remaining inconsistency. **Confidence:
0.88.**

**Verdict:** CONSISTENT.

## Axis 9: Adversarial

**Challenge:** What if the consumer edits `environment:` AND changes
other inputs simultaneously? Does the destroy-then-apply still work?

**Response:** Yes. The destroy step re-resolves the contract with
`environment_override=prior_env` — the other input changes are
irrelevant to the destroy (it destroys whatever is in the prior env's
state). The new apply resolves with the new env + new inputs. The two
operations are independent. **Confidence: 0.85.**

**Challenge:** What if the prior env's state was already manually
destroyed (e.g., via decommission)?

**Response:** `terraform destroy` against an empty state is a no-op
(exits 0). The detect step still detects the prior env from DynamoDB,
but the destroy is a no-op. The apply proceeds. This is correct
behavior — no false failure. **Confidence: 0.88.**

**Verdict:** ROBUST.

---

## Binding revisions applied

1. **R1 (docs):** Add to RESEARCH.md pitfalls: the destroy step must
   inject `deletion_protection=false` into the contract inputs before
   re-resolving with `environment_override=prior_env`. Without this,
   `prevent_destroy` lifecycle blocks (REQ-86) block the destroy. This
   is already noted in RESEARCH §5 pitfall 3 and PLAN P2 implementation
   note 3. No change needed — already captured.

2. **R2 (docs):** Clarify in PLAN P2 that the `record_applied_env` SK
   format is `contractId#LAST_APPLIED#<timestamp>` so the detect step
   can query `begins_with "contractId#LAST_APPLIED#"`. This is already
   in RESEARCH §2 and GRILL Axis 4. No change needed — already captured.

## Escalations

None. All challenges resolved at full autonomy.