# CLARIFY — v1.28 CLI Canonicalization + Identity Layer

> **Autonomy:** full. Auto-resolution with assumption logging per
> `config.autonomy.level: "full"`. No human escalation unless confidence
> < 0.60. The user-approved re-mapping plan (v1.18 spec → v1.28) resolved
> the headline discrepancy. This file records the remaining ambiguities
> and the grounding gaps surfaced in pre-flight.

---

## Method

The clarify stage identifies ambiguities in the v1.28 specification and
resolves them at full autonomy. The v1.28 spec is the user-provided
"Universal Feature Specification — v1.18 CLI Canonicalization + Identity
Layer," re-mapped to v1.28 (milestone number, tag line, and all
ID namespaces) per the user-approved plan. Each ambiguity gets a
decision ID (D-226+, continuing from v1.27's D-214..D-225), a resolution,
a confidence score, and a rationale.

---

## Prior-conversation resolutions (already locked, restated for the record)

These were resolved by the user-approved re-mapping plan in the
conversation that spawned v1.28. They are load-bearing for v1.28
execution.

### Q-P1 — The source spec is titled "v1.18" but v1.18 already shipped. What milestone is this?

**Resolution:** Re-map the spec's *content* (CLI Canonicalization +
Identity Layer) to **v1.28**, the next milestone after v1.27 (complete).
Tags run on the **v1.27.x** line (P0 = `v1.27.0`). Milestone branch:
`milestone/v1.28-cli-identity`.
**Confidence:** 1.0 (user-confirmed — "Re-map to v1.28"). **Decision:** n/a (milestone identity, not a D-ID).

### Q-P2 — The spec's "locked inputs" (D-NEW-26, kj engine, Nova-idp, INV-63/64/65, CAP-025..030, REQ-001..031) don't exist in the repo. How to handle?

**Resolution:** Author them fresh in this milestone's CLARIFY/RESEARCH as
**D-226..D-231, INV-12..17, CAP-033..038, REQ-323..353**. The `kj` engine
is mapped to the existing **kyverno-json** engine (INV-4 swappable) — no
new engine is built. CAP/INV/REQ IDs are re-allocated to avoid collisions
with shipped history (CAP-025..032 and INV-1..11 are blockchain/pilot).
**Confidence:** 1.0 (user-confirmed — "Re-map to v1.28"). **Decision:** D-227 (kj→kyverno-json), plus the ID-allocation block in REQUIREMENTS.md.

### Q-P3 — The spec claims a "Cognito drop." No Cognito exists in the repo. What does NFR-5 mean?

**Resolution:** NFR-5 (no AWS-managed identity in the path) is a
**greenfield constraint**, not a migration. Nova-idp is built fresh; no
Cognito/IAM Identity Center is *introduced*. The "drop" framing is
aspirational language from the source spec, not a literal removal.
**Confidence:** 1.0. **Decision:** D-226 (recorded below; NFR-5 restated
as a greenfield constraint in INV-15).

---

## Open questions from the spec's §7 (auto-resolved at full autonomy)

### Q1 — Argon2 native dependency in Lambda runtime

`argon2-cffi` has a C extension that may not build cleanly in the Lambda
Python 3.12 runtime.

**Resolution (D-228):** Use `argon2-cffi` with bundled wheels; if the
extension fails to load, fall back to the pure-Python implementation. If
both fail, document the Fargate migration path for the auth Lambda.
CAP-036 covers end-to-end verification.
**Confidence:** 0.85. **Rationale:** Bundled wheels are the standard
workaround for Lambda native deps; the pure-Python fallback is a safe
degradation. Fargate is the escape hatch if Lambda's runtime is
fundamentally incompatible. RESEARCH will validate wheel availability for
Python 3.12 + the Lambda execution environment.
**Impact if wrong:** Auth Lambda migrates to Fargate, adding ~1 week to P2.

### Q2 — PAT revocation propagation latency

The 60-second SLO (NFR-4) depends on whether the token-vend Lambda reads
PAT revocation state from DynamoDB on every request (eventually
consistent reads) or via a cached/denylist mechanism.

**Resolution (D-229):** Read-on-every-request with strongly consistent
reads on the PAT hash table. Cost is acceptable given expected request
volume (token vending is not a hot path — it precedes a deploy, not every
request). REV-351 verifies the SLO in CI.
**Confidence:** 0.90. **Rationale:** Strongly consistent DynamoDB reads
have single-digit-ms latency at expected volume; the 60s SLO has >10x
headroom. A cache layer adds invalidation complexity that the SLO does
not require.
**Impact if wrong:** If read latency exceeds 60s under load, introduce a
DynamoDB TTL + cache layer; SLO must be re-verified.

### Q3 — JWKS endpoint: Lambda function URL vs. API Gateway

A function URL is simpler and cheaper but lacks throttling, WAF, and
custom domains out of the box.

**Resolution (D-230):** Start with a Lambda function URL behind a custom
domain; rate limiting configured at the DNS/CDN layer. API Gateway
migration deferred to v1.19+ if throttling requirements grow.
**Confidence:** 0.80. **Rationale:** The JWKS endpoint is public-key
only (no secrets); the threat surface is low. Function URL + CDN rate-
limiting covers the v1.28 volume. API Gateway is over-engineering until
traffic patterns are known.
**Impact if wrong:** If throttling becomes a requirement, API Gateway
migration adds ~3-5 days.

### Q4 — Mode resolver precedence with invalid `NOVA_CLIENT_MODE` value

What happens if the env var is set to something other than `agent` or
`interactive` (e.g., `NOVA_CLIENT_MODE=auto`)?

**Resolution (D-226):** Invalid env var values are ignored, falling
through to credential type. A warning is logged. Behavior is documented
in the `nova-cli` README. This is a sub-clause of the mode-resolution
priority decision.
**Confidence:** 0.90. **Rationale:** Ignoring + warning is the least
surprising behavior for an operator debugging mode issues. Failing hard
would block legitimate workflows that set a stale/typo'd env var.
**Impact if wrong:** Operators debugging mode issues may be confused;
non-blocking.

### Q5 — Service-account PAT vs. developer PAT in the same session

What if both credential types are available (e.g., a developer explicitly
exports a service-account PAT)?

**Resolution (D-226):** The most recently acquired credential wins.
Documented in `nova auth login` output. The credential type is what
drives mode resolution (INV-14), so the operator sees which mode was
selected and why.
**Confidence:** 0.85. **Rationale:** "Most recent wins" is the simplest
deterministic rule that matches operator mental models of "I just logged
in as X." The audit event records the winning credential type, so the
selection is traceable.
**Impact if wrong:** Mode selection may surprise the operator; non-
blocking, but `nova auth status` must make the active credential explicit.

### Q6 — ABAC policy ownership and versioning

`platform/abac/token-vend.policy` is referenced, but who owns changes?
How are policy versions tracked in audit?

**Resolution (D-231):** Policy changes require PR review; the policy
version (git SHA) is recorded in every token-vend audit event. Owner:
Platform Security. The policy file lives in the platform repo at
`platform/abac/token-vend.policy` and is reviewed like any other
production config.
**Confidence:** 0.90. **Rationale:** Git SHA is the natural version
identifier for a repo-resident policy; recording it in the audit event
makes every allow/deny decision reconstructable to the exact policy text.
**Impact if wrong:** Untracked policy changes could lead to unexpected
allow/deny decisions in production, undermining audit defensibility.

---

## Grounding gaps surfaced in pre-flight (auto-resolved)

### G1 — The `kj` engine does not exist; the spec treats it as locked.

**Resolution (D-227):** The token-vend Lambda uses the existing
**kyverno-json** engine (INV-4 swappable) as the ABAC evaluator. The
policy at `platform/abac/token-vend.policy` is a kyverno-json policy.
No new `kj` engine is built in v1.28. If a distinct `kj` engine is
desired later, it is a separate research spike (not this milestone).
**Confidence:** 0.95. **Rationale:** The repo already has a swappable
policy engine (INV-4) implemented as kyverno-json. Building a second
engine to do the same job violates the swappable-engine invariant's
spirit. kyverno-json's `evaluate` semantics cover the spec's ABAC needs
(subject, claims, resource, environment → allow/deny).
**Impact if wrong:** If the user actually wants a new `kj` engine, v1.28
scope expands significantly (engine design + implementation + migration).
This was flagged as caveat #3 in the approved plan; the recommended path
(kyverno-json) is locked here.

### G2 — The spec's INV-18..21, INV-34, INV-63/64/65 don't exist.

**Resolution:** Re-allocated as **INV-12..INV-17** (see REQUIREMENTS.md
§v1.28 Invariants). The 1:1 mapping:
- INV-63 (mode observability) → INV-12
- INV-64 (mode determinism) → INV-13
- INV-65 (credential type encodes role) → INV-14
- INV-18..21 (attestation invariants) → INV-15 (no AWS-managed identity),
  INV-16 (password storage), INV-17 (ABAC discipline). The spec's
  attestation invariants INV-18..21 are partially covered by existing
  invariants (INV-6 immutable audit) + INV-17; the JWS-from-PAT behavior
  (REQ-332) is a requirement, not a separate invariant, in this mapping.
- INV-34 (MFA enforcement) → deferred to v1.21+ (out of scope per §2.2);
  no INV allocated in v1.28.
**Confidence:** 0.85. **Rationale:** The mapping preserves the spec's
intent without colliding with the repo's INV-1..11. INV-34 (MFA) is
explicitly deferred per the spec's own §2.2 out-of-scope table.
**Impact if wrong:** If the user wants the exact INV-18..21 semantics as
separate invariants, INV-12..17 can be re-numbered; non-blocking.

### G3 — The spec's CAP-025..030 collide with blockchain/pilot CAPs.

**Resolution:** Re-allocated as **CAP-033..CAP-038** (see REQUIREMENTS.md
§v1.28 + REQ-352). The 1:1 mapping:
- CAP-025 (CLI subcommand surface) → CAP-033
- CAP-026 (subcommand delegates to core/) → CAP-034
- CAP-027 (layer matches wheel) → CAP-035
- CAP-028 (Nova-idp auth flow) → CAP-036
- CAP-029 (token-vend signs via KMS) → CAP-037
- CAP-030 (PAT issuance + revocation) → CAP-038
**Confidence:** 1.0. **Rationale:** Existing CAP-025..032 are
blockchain/pilot capabilities (STATE.md); re-use would corrupt the
capability registry. The re-allocated IDs are the next available.
**Impact if wrong:** None — this is a numbering decision, not a semantic
one.

### G4 — The spec's REQ-001..031 collide / don't exist.

**Resolution:** Re-allocated as **REQ-323..REQ-353** (1:1 with the spec's
REQ-001..031). Full text in REQUIREMENTS.md §v1.28. Max existing REQ =
REQ-322.
**Confidence:** 1.0. **Rationale:** Same as G3 — avoid collision, use
next available range.

### G5 — `platform/abac/`, `nova/` subcommand dir, `nova-idp-*` Lambdas don't exist.

**Resolution:** These are **greenfield deliverables** of v1.28 execution
phases, not pre-existing "locked architectures." RESEARCH will design
them; PLAN will sequence them; EXECUTE will build them. The spec's
"Operating Principle 1" (incremental delivery) is honored — v1.28 is
net-new work.
**Confidence:** 1.0. **Rationale:** The spec itself describes these as
new ("introducing Nova-idp"). The mis-framing was in calling them
"locked" — they are locked in *scope*, not in *prior existence*.
**Impact if wrong:** None — this is a framing correction.

---

## Decision ledger (v1.28 — D-226..D-231)

| ID | Title | Confidence | Load-bearing for |
|----|-------|------------|------------------|
| D-226 | Mode resolution priority + invalid-env + dual-credential | 0.90 | REQ-327, INV-12, INV-13, INV-14 |
| D-227 | ABAC engine = kyverno-json (no `kj` engine built) | 0.95 | REQ-336, REQ-339, INV-17, NFR-9 |
| D-228 | Argon2id in Lambda: bundled wheels + pure-Python fallback + Fargate path | 0.85 | REQ-333, REQ-334, INV-16, NFR-8 |
| D-229 | PAT revocation: strongly-consistent DDB read-on-every-request, 60s SLO | 0.90 | REQ-342, REQ-343, REQ-351, NFR-4 |
| D-230 | JWKS endpoint: Lambda function URL + custom domain + CDN rate-limit | 0.80 | REQ-338, NFR-5 |
| D-231 | ABAC policy ownership: Platform Security, git SHA in audit | 0.90 | REQ-339, NFR-9 |

---

## Assumptions logged (full autonomy, no human escalation)

1. **CodeArtifact is provisionable** in AWS account `581513795199` (the
   pilot account). RESEARCH will confirm IAM permissions + repository
   creation. If not, v1.28 falls back to a private PyPI server or a
   Gitea-hosted wheel index; the CLI subcommand surface (REQ-324) and
   identity layer (REQ-333+) are unaffected.
2. **Python 3.12** is the target runtime for both the CLI wheel and the
   Lambda functions (spec §4 REQ-004.3). The repo's current Python
   version will be confirmed in RESEARCH; if it differs, the CLI pins
   3.12 and Lambda uses the 3.12 runtime regardless.
3. **KMS asymmetric signing** (RSA-2048 or ECDSA P-256) is available in
   the target account. RESEARCH will confirm. If only symmetric KMS is
   available, the token-vend Lambda uses symmetric signing + a public-key
   publication step (less ideal, but functional); INV-15 is unaffected.
4. **The Forge action** (REQ-326) is the existing `nova cli-action`
   pattern, extended to both GitHub and Gitea marketplaces. The repo's
   current Forge/Gitea workflow conventions (`.gitea/workflows/`,
   `deploy.yml@v1.25`) are the baseline.
5. **MFA/TOTP** code path ships in v1.28 (per spec §2.2) but enforcement
   for prod/dr is deferred to v1.21+. This is a doc/test-only path in
   v1.28 — no enforcement gate.

---

## CLARIFY complete

All material ambiguities resolved at full autonomy (6 open questions +
5 grounding gaps → D-226..D-231, confidence ≥ 0.80). No human escalation
triggered (all confidences ≥ 0.60 threshold). REQUIREMENTS.md updated
with the decision ledger + invariants. Next: RESEARCH.

---

# CLARIFY — v1.29 Reposplit + Identity Layer Bring-Live

> **Autonomy:** full. Auto-resolution with assumption logging per
> `config.autonomy.level: "full"`. No human escalation unless confidence
> < 0.60. The v1.29 spec is v1.1 (highly detailed — §7 resolves Q1-6, Q7
> carried forward as a verification-gate dependency). This file records
> the v1.29 ambiguities and the scope-split grounding.

---

## Method

The v1.29 spec ("Universal Feature Specification — Reposplit + Identity
Layer Bring-Live", v1.1) is the most detailed spec the project has
received: it includes BDD acceptance criteria, an 8-item M1.5 spike
checklist, 7 decisions pre-drafted (D-232..238), 14 NFRs, and an
explicit §7 resolving Q1-6. Clarify work focuses on (a) the scope split
between `acdl` (CIAgent) and `nova-platform-ops` (out-of-band), (b) the
`kj` identity (Go binary vs. the v1.28 kyverno-json re-mapping), and (c)
the carried-forward Q7. Each ambiguity gets a decision ID (D-232+,
continuing from v1.28's D-226..D-231), a resolution, a confidence score,
and a rationale.

---

## Prior-conversation resolutions (already locked, restated for the record)

These were resolved by the user-approved execution plan in the
conversation that spawned v1.29.

### Q-P1 — The spec creates a separate repo `nova-platform-ops`. CIAgent runs inside `acdl`. Where does the Terraform code land?

**Resolution:** Terraform modules
(`networking`/`kms`/`identity`/`contract-ingest`/`bootstrap`/`edge`) are
authored **out-of-band** in `nova-platform-ops` (operator-owned). CIAgent
in `acdl` delivers only the acdl-side work (publish.yml, Gitea scrub,
CFN archive, operator guide, consumer bump) and tracks the ops-side
REQs as **covered-reference** (verification surface = the M1/M1.5/M2
cutover gates documented in the operator guide).
**Confidence:** 1.0 (user-confirmed — "Author out-of-band in
nova-platform-ops"). **Decision:** scope split documented in
PROJECT.md §v1.29 + REQUIREMENTS.md §v1.29.

### Q-P2 — The run scope. How far does this `/ci-run` go?

**Resolution:** Full milestone through the final phase (P0 → P1..P5 →
P6 final review + audit + milestone ship, tag `v1.28.6`).
**Confidence:** 1.0 (user-confirmed — "Full milestone through final
phase"). **Decision:** n/a (execution scope, not a D-ID).

### Q-P3 — Edge 8 / REQ-354 footnote: pilot consumer deploy bump. Handle how?

**Resolution:** Include a cross-project phase (P5) in this CIAgent run
(multi-project mode is active). Bump `nova-blockchain-exchange`
deploy.yml `@v1.25` → `@v1.29` + smoke test.
**Confidence:** 1.0 (user-confirmed — "Cross-project phase in this
run"). **Decision:** n/a (execution scope).

---

## Spec-grounded resolutions (from §7 + §5)

### Q1 — State bucket bootstrap on day-0 (resolved per spec §7.1)

**Resolution:** Manual one-time at the operator's secure scratch; Terraform
then adopts it via `terraform import`. Avoids bootstrapping the
bootstrapper. **Confidence:** 1.0 (spec §7.1 explicit). **Decision:**
D-235 (tag-pin handoff) — the state bucket is one of the imported
resources.

### Q2 — `pyproject.toml` version bump (resolved per spec §7.2)

**Resolution:** Bump to `1.29.0` in M1 (P2 — Gitea scrub phase) of v1.29
alongside the Gitea scrub. **Confidence:** 1.0 (spec §7.2 explicit).
**Decision:** n/a (implementation detail, tracked in PLAN.md P2).

### Q3 — WAF cost (resolved per spec §7.3)

**Resolution:** Acceptable for the JWKS public surface; documented in
operator-guide cost section (~$5–10/month per WebACL + per-request).
**Confidence:** 1.0 (spec §7.3 explicit). **Decision:** documented in
REQ-OPS-GUIDE AC.

### Q4 — Coverage 73.8% — does this milestone drive it down further? (resolved per spec §7.4)

**Resolution:** Accept any further debt as carry-forward to the separate
NFR milestone. New modules have ≥80% coverage; older code paths are
unchanged. YELLOW carried without scope expansion. **Confidence:** 1.0
(spec §7.4 explicit). **Decision:** n/a (NFR carry-forward, not a v1.29
D-ID).

### Q5 — CFN code deletion timing (resolved per spec §7.5)

**Resolution:** Archive to `docs/archive/nova-idp-cfn-v1.28.md`; deletion
is a follow-up after the next pilot run verifies Terraform parity.
**Confidence:** 1.0 (spec §7.5 explicit). **Decision:** REQ-369 AC (3).

### Q6 — `acdl-act-runner-role` reuse (resolved per spec §7.6)

**Resolution:** Reuse the existing role for v1.29 to minimize IAM surface
changes; scope narrow per REQ-360. **Confidence:** 1.0 (spec §7.6
explicit). **Decision:** covered by REQ-360 (IAM-NARROW).

### Q7 — `kj` image verification dependency (CARRY-FORWARD per spec §7.7)

**Resolution (carry-forward):** M1 cutover is conditional on the M1.5
verification gate. **Recommendation:** Block M1 cutover until M1.5
passes. If M1.5 fails three consecutive rebuilds, defer to M2a and ship
Nova-idp in read-only partial mode (no token issuance) until `kj` is
verified. **Impact if wrong:** A live token-vend that signs with a
broken ABAC path would let through a denied claim — fails closed only if
`ImageUri` is verified pre-apply. **Confidence:** 0.92 (spec §7.7
explicit + D-236 cutover shape). **Decision:** D-236 (cutover shape +
rollback procedure). This is the **only** outstanding carry-forward;
CIAgent in acdl builds + publishes the image + the gate tests (P1), but
the live 3-rebuild verification happens in `nova-platform-ops` CI
(out-of-band). CIAgent does not block on it.

---

## Grounding-gap resolutions (surfaced in pre-flight)

### G1 — The spec's `kj` vs. v1.28's `kj` re-mapping

**Ambiguity:** v1.28 (D-227) re-mapped the spec's `kj` engine →
kyverno-json (INV-4 swappable), explicitly stating "no new `kj` engine
is built." v1.29 reintroduces `kj` as a compiled Go binary
(`platform/abac/kj-version.txt`, pinned v0.0.3) embedded in an ECR
container image. Is this a contradiction?

**Resolution:** No contradiction. v1.28's `kj` was a *policy engine*
reference; v1.29's `kj` is a *compiled Go binary* (a distinct artifact).
The kyverno-json engine remains the policy engine (INV-4). The v1.29
`kj` binary is invoked via `subprocess.run(['/opt/kj/kj', 'apply', ...])`
by the Lambda handler — it is a **substrate** binary, not a policy
engine. The two coexist: kyverno-json evaluates ABAC policy; `kj` is the
container image's static binary that the Lambda runtime executes. No
collision.
**Confidence:** 0.95 (spec §3.3 Edge 5 item 4 explicit + v1.28 D-227
scope). **Decision:** documented in PROJECT.md §v1.29 ID allocations +
KJ-STATIC NFR.

### G2 — `REQ-363b` sub-requirement numbering

**Ambiguity:** The spec uses `REQ-363b` for the Fargate defensive
fallback. The repo's REQ namespace is `REQ-NNN` (numeric). How to
record `363b`?

**Resolution:** Keep `REQ-363b` as-is (sub-requirement of REQ-363). It
is a distinct requirement (Fargate fallback, KJ-LOCKSTEP) but logically
paired with REQ-363 (production substrate). The `b` suffix is
unambiguous and matches the spec. No collision with any existing REQ.
**Confidence:** 0.98 (spec explicit + no collision). **Decision:** n/a
(naming convention).

### G3 — `REQ-370` gap

**Ambiguity:** The spec jumps from REQ-369 to REQ-371. Is REQ-370
missing or intentionally unused?

**Resolution:** Intentionally unused per the source spec. REQ-370 is a
gap in the spec's numbering (likely a deleted/renumbered item during
spec v1.0 → v1.1). v1.29 does not allocate REQ-370; it remains a
reserved gap. **Confidence:** 0.90 (spec explicit gap, no content).
**Decision:** n/a (spec fidelity).

### G4 — Covered-reference REQs and CIAgent verification

**Ambiguity:** REQ-355, 356, 357, 358, 359, 360, 361, 362, 363, 363b,
364, 365, 366, 371 are authored in `nova-platform-ops` (out-of-band).
How does CIAgent verify them? Are they `human_needed`?

**Resolution:** They are **covered-reference**, NOT `human_needed`. The
verification surface is the M1/M1.5/M2 cutover gates documented in the
operator guide (`docs/operator-guide-platform-ops.md`). The operator
guide lists each covered-reference REQ with its cutover gate entry
(M1/M1.5/M2). CIAgent verify marks them `covered-reference` and the
final-phase audit confirms the operator guide documents all gates.
**Confidence:** 0.94 (scope-split decision + spec §2.3 milestone
gates). **Decision:** documented in REQUIREMENTS.md §v1.29 + REQ-OPS-
GUIDE AC.

---

## Assumptions (logged, not escalated — confidence ≥ 0.80)

1. **`kj` v0.0.3** is available at the pinned SHA in
   `platform/abac/kj-version.txt` and compiles with `CGO_ENABLED=0
   GOOS=linux GOARCH=amd64`. RESEARCH will confirm the source repository
   + build commands. If the binary is not available, P1 (publish
   pipeline) cannot produce the ECR image; M1.5 gate fails by
   construction → M2a (Fargate toggle, same image) also fails → escalate
   (but this is a spec dependency, not a CIAgent ambiguity).
2. **ECR repository** exists or is creatable in account `581513795199`
   for the `kj` image. RESEARCH will confirm. The repo name is not
   specified in the spec; the operator guide will document it.
3. **GitHub Releases** is the artifact distribution channel (per
   REQ-354). The `acdl/acdl` repo is already on GitHub (the Gitea scrub
   in REQ-367 standardizes on GitHub). NOVA_FORGE_TOKEN (Gitea) is
   retained for `nova-platform-ops` releases only.
4. **The `nova idp setup --apply` terraform-delegation** (REQ-369 AC 2)
   requires `terraform` to be on the operator's PATH. The CLI detects
   terraform via `which terraform`; if absent, it falls back to the CFN
   path with a deprecation warning (the CFN archive remains read-only
   reference, but the delegation is the preferred path).
5. **The M1.5 8-item spike** (spec §3.3 Edge 5) is the verification
   gate. CIAgent in acdl authors the *tests* (test_idp_auth,
   test_kms_roundtrip, ABAC E2E) in P1; the *live 3-rebuild run*
   happens in `nova-platform-ops` CI. This is the Q7 carry-forward
   surface.

---

## CLARIFY complete

All material ambiguities resolved at full autonomy (3 prior-conversation
+ 7 spec-grounded + 4 grounding-gap → D-232..D-238, confidence ≥ 0.80).
Q7 is the only carry-forward (verification-gate dependency, not a
blocking ambiguity). No human escalation triggered (all confidences ≥
0.60 threshold). REQUIREMENTS.md updated with the decision ledger +
invariants + NFR constraints. Next: RESEARCH.