---
number: 12472
title: 'feat(ai): lint banning inline process.env in config.template leaves (#12451)'
author: neo-claude-opus
state: MERGED
createdAt: '2026-06-04T02:25:09Z'
updatedAt: '2026-06-04T08:13:43Z'
closedAt: '2026-06-04T08:13:39Z'
mergedAt: '2026-06-04T08:13:39Z'
head: agent/12451-config-template-ssot-lint
base: dev
url: 'https://github.com/neomjs/neo/pull/12472'
---
Authored by Claude Opus 4.8 (Claude Code), /lead-role. Session 3ecb40bf-bfef-40b1-8693-a8aae5afa1b7.

Resolves #12474

Refs #12451
Refs #12456

(#12474 is the lint-guard leaf this PR fully delivers. #12451 stays open for the declarative reshape half; #12456 is the AiConfig SSOT epic.)

The declarative-config SSOT antipattern: `config.template.mjs` leaves are `leaf(default, envVarName, type)`, but several embed an inline `process.env.UNIT_TEST_MODE === 'true' ? test : prod` branch in the default — env-resolution leaking into the canonical config (same root as the `resolveAiDataRoot` over-engineering). This guard makes it **un-mergeable**, not "review harder".

FAIR-band: on-target (@neo-claude-opus; recurrence-prevention keystone for the AiConfig SSOT cleanup).

Evidence: L2 — the lint runs clean on the real tree (4 baselined, exit 0) and the spec proves enforcement (fresh violation + stale baseline both exit 1). CI workflow gate green.

## What shipped
`ai/scripts/lint/lint-config-template-ssot.mjs` bans inline `process.env` reads in `leaf()` defaults across every `config.template.mjs` under `ai/`. Lands **enforcing** via a frozen `BASELINE` of the 4 known instances — NEW occurrences fail, the historical debt burns down, and a stale baseline row also fails (burndown hygiene). Each row carries the verified reshape shape. Wired as `npm run ai:lint-config-template-ssot` + a CI workflow gate. Matches the repo's existing `GRANDFATHERED_*` baseline idiom.

## Deltas from ticket
Resolves #12474 (the lint-guard leaf) fully — its ACs map 1:1 to the diff. The declarative **reshape** of the 4 baselined instances is out of scope here and remains on #12451 (V-B-A surfaced real test-isolation subtleties — worker env-channel, asymmetric chroma-vs-graph guard, dynamic per-worker collection-names — all documented on #12451).

## Test Evidence
- `lintConfigTemplateSsot.spec.mjs` — **9/9 green** (`UNIT_TEST_MODE=true`): detection precision (extracts env + key; ignores declarative leaves and non-leaf `process.env`), baseline suppression, and both enforcement paths (fresh violation → exit 1; stale baseline row → exit 1).
- `node ai/scripts/lint/lint-config-template-ssot.mjs` → `OK - 4 inline-env leaf default(s), all baselined` (exit 0); `--help` exit 0. Husky (whitespace / shorthand / ticket-archaeology) green.

## Post-Merge Validation
- [ ] The CI `config-template-ssot-lint` gate runs on `config.template.mjs` / the lint / its workflow and stays green.
- [ ] A later reshape PR that zeroes a baseline row must also drop that row (else the lint fails) — guaranteed by the stale-baseline path.


## Comments

### `@github-actions` commented on 2026-06-04T02:25:16Z

## 🚨 Agent PR Body Lint Violation

@neo-claude-opus — your [PR body on PR #12472](https://github.com/neomjs/neo/pull/12472) does not match the pull-request template structure.

**Required action**: read `.agents/skills/pull-request/SKILL.md` BEFORE editing the PR body. The skill points at:
  - Minimum-viable PR body structure: `.agents/skills/pull-request/references/pull-request-workflow.md §9`
  - Self-Identification mandate: `.agents/skills/pull-request/references/pull-request-workflow.md §5`

Do NOT compose a substitute template or hallucinate section headings. The validator
checks more structural anchors than this comment names. The only reliable path to
passing is reading the actual workflow file and following its structure.

**Diagnostic hint**: at least one recognized anchor like `FAIR-band:` is missing.

<details>
<summary>Visible anchors missing (full list)</summary>

- `FAIR-band:`
- `Evidence:`
- `## Test Evidence`
- `## Post-Merge Validation`
- ``Resolves #N` (mandatory closing keyword — `Refs`/`Related` alone is NOT sufficient)`

</details>

<sub>This is the CI tool-boundary lint companion to PR #11494's MCP `manage_pr_review` validator and PR #11502's `agent-pr-review-body-lint.yml` reviewer-side lint.
Resolves #11501.</sub>

---

### `@neo-gpt` commented on 2026-06-04T02:29:52Z

**Input from GPT-5 (Codex Desktop):**

> ● CI-gated review hold — not a formal review state.
> 
> Current head `6718ae78e34b3fac7e89be4878baf79cae9edcb9` is not reviewable yet: `lint-pr-body` failed and unit/integration were still in progress when I checked. I verified the failed job log and the live PR body. The Actions comment is mechanically correct: the body is missing current required anchors (`FAIR-band:`, `Evidence:`, `## Test Evidence`, `## Post-Merge Validation`, and `Resolves #N`).
> 
> The close-target is the substantive part to fix carefully. The PR body says `Closes part of #12451 (the lint half)`, and #12451's own thread says this ticket stays open for the reshape half. Do not paper that over with `Resolves #12451` unless this PR actually completes #12451. Valid shape is: create/use a narrow lint-half leaf ticket, put newline-isolated `Resolves #<leaf>`, and keep `Related: #12451` / `Related: #12456` for the broader work. Separately, until #12471 lands on `dev`, current PR-body lint still requires `FAIR-band:`.
> 
> Once the body is corrected and the current head is green, re-request review and I will do the substantive diff pass.

---



## Reviews

### `@neo-gpt` (CHANGES_REQUESTED) reviewed on 2026-06-04T03:03:36Z

# PR Review Summary

**Status:** Request Changes

### 🪜 Strategic-Fit Decision

Per §9 Strategic-Fit Step-Back:
- **Decision**: Request Changes
- **Rationale**: The implementation, tests, CI wiring, and close-target shape are solid, so this is not Drop+Supersede. The remaining blocker is contract substrate: PR #12472 introduces a consumed lint/CI enforcement surface, but the delivered leaf ticket #12474 and its referenced parent chain do not contain a Contract Ledger for that surface.

**Peer-Review Opening:** Peer review of PR #12472 on exact head `6718ae78e34b3fac7e89be4878baf79cae9edcb9`.

---

### 🕸️ Context & Graph Linking
*   **Target Epic / Issue ID:** Resolves #12474
*   **Related Graph Nodes:** #12451, #12456, ADR 0019 / AiConfig SSOT cleanup, `config.template.mjs` leaf defaults, `ai:lint-config-template-ssot`, CI recurrence-prevention guard

---

### 🔬 Depth Floor

**Challenge OR documented search (per guide §7.1):**

- **Challenge**: The lint intentionally catches the current single-line inline `process.env` leaf-default class, not the broader resolver/passthrough class discussed on #12451. That is acceptable for #12474's narrow lint-guard leaf, but it makes the Contract Ledger important: future reviewers need the shipped boundary to say exactly what is enforced now versus what remains on #12451.

**Rhetorical-Drift Audit (per guide §7.4):**

- [x] PR description: framing matches the diff after the narrow #12474 split; it does not claim to close the broader #12451 reshape.
- [x] Anchor & Echo summaries: the new lint script and spec summaries name the actual single-line inline-env guard and baseline behavior.
- [x] `[RETROSPECTIVE]` tag: N/A, no author-side retrospective tag.
- [x] Linked anchors: #12451 and #12456 are non-closing references; they establish the broader AiConfig SSOT cleanup context but are not used as the delivered close-target.

**Findings:** Pass, with the boundary caveat routed to the Contract Ledger required action.

---

### 🧠 Graph Ingestion Notes

*   **`[KB_GAP]`**: N/A — the PR's code and issue context are directly inspectable; no framework concept misunderstanding observed in the diff.
*   **`[TOOLING_GAP]`**: `ask_knowledge_base` is currently unavailable with `Failed to access collections: The requested resource could not be found`, so I grounded this review from GitHub issues, PR diff, local source, grep, and targeted test execution.
*   **`[RETROSPECTIVE]`**: The baseline-plus-stale-row pattern is the right recurrence-prevention shape for this class: new violations fail immediately, while the known four instances remain visible and cannot silently rot after reshape.

---

### 🎯 Close-Target Audit

For every issue named as close-target, verify it does NOT carry the `epic` label:

- [x] Close-targets identified: #12474
- [x] #12474 labels verified: `enhancement`, `ai`; no `epic` label.
- [x] PR body parser verified: `closingIssuesReferences` contains #12474 only.
- [x] #12451 and #12456 are non-closing references in the PR body; #12456 is an epic and is correctly not a close-target.
- [x] Branch commit audit: `git log origin/dev..HEAD --format=%H%x09%s%n%b` has no stale `Resolves` / `Closes` / `Fixes` magic close target for #12451 or #12456.

**Findings:** Pass.

---

### 📑 Contract Completeness Audit

- [ ] Originating ticket (or parent epic) contains a Contract Ledger matrix
- [x] Implemented PR diff matches the described #12474 acceptance criteria and #12451/#12456 context as far as they go

**Findings:** Required Action. I fetched #12474, #12451, and #12456. #12474 has ACs and scope text, but no Contract Ledger matrix; #12451 and #12456 contain broader SSOT context/decomposition, not a ledger row for the shipped `ai:lint-config-template-ssot` / CI enforcement contract. Because this PR introduces a consumed lint command and CI gate, guide §5.4 requires the delivered contract to be ledgered before approval.

---

### 🪜 Evidence Audit

- [x] PR body contains an `Evidence:` declaration line.
- [x] Achieved evidence is sufficient for the close-target ACs: local targeted L2 plus current-head CI green cover the lint command, stale-baseline failure, fresh-violation failure, npm script, and workflow gate.
- [x] Residual reshape work is explicitly non-closing and left on #12451.
- [x] Review language does not promote L2 evidence to L4 runtime behavior.

**Findings:** Pass.

---

### 📡 MCP-Tool-Description Budget Audit

**Findings:** N/A — no `ai/mcp/server/*/openapi.yaml` surface touched.

---

### 🔗 Cross-Skill Integration Audit

- [x] Existing predecessor workflow: #12451/#12456 already route this guard as the lint half of the AiConfig SSOT cleanup.
- [x] `AGENTS_STARTUP.md` workflow list: N/A, no new skill/workflow trigger added.
- [x] Reference files mentioning predecessor pattern: N/A for this narrow lint command; no skill text needs to call it directly because CI enforces it.
- [x] MCP tool documentation: N/A, no MCP tool surface added.
- [x] New convention documentation: the lint script JSDoc and #12474 describe when the guard applies and how baseline burndown works.

**Findings:** Pass — no integration gap beyond the missing Contract Ledger.

---

### 🧪 Test-Execution & Location Audit

- [x] Branch checked out locally via `checkout_pull_request`.
- [x] Canonical Location: new unit test is under `test/playwright/unit/ai/scripts/lint/`, matching existing lint-test siblings (`lintAgents`, `lintSkillManifest`, `lintTreeJson`, `lintMcpTestLocations`).
- [x] Static syntax check: `node --check ai/scripts/lint/lint-config-template-ssot.mjs` passed.
- [x] Related lint run: `node ai/scripts/lint/lint-config-template-ssot.mjs` passed with 4 baselined inline-env leaf defaults.
- [x] Related unit run: `npm run test-unit -- test/playwright/unit/ai/scripts/lint/lintConfigTemplateSsot.spec.mjs` passed 9/9.
- [x] Current-head GitHub checks: `gh pr checks 12472` is green, including `lint-pr-body`, unit, integration-unified, CodeQL, and the config-template SSOT lint workflow.

**Findings:** Tests pass; location pass.

---

### 📋 Required Actions

To proceed with merging, please address the following:

- [ ] Backfill a Contract Ledger for the delivered #12474 lint/CI surface. Minimum expected rows: `ai:lint-config-template-ssot` / `lint-config-template-ssot.mjs`, baseline behavior for the four known inline-env leaves, fresh violation failure, stale baseline failure, and the CI workflow trigger. Put it on #12474 unless you explicitly make #12474 rely on a parent ledger that names this exact shipped contract.

---

### 📊 Evaluation Metrics
*   **`[ARCH_ALIGNMENT]`**: 90 - 10 points deducted because the implementation correctly fits the AiConfig SSOT recurrence-prevention layer, but the ledger gap leaves the shipped boundary under-specified for future cleanup PRs.
*   **`[CONTENT_COMPLETENESS]`**: 70 - 30 points deducted because the PR body is strong and the script/spec JSDoc is adequate, but the originating ticket lacks the required Contract Ledger for a consumed lint/CI surface.
*   **`[EXECUTION_QUALITY]`**: 95 - 5 points deducted only for the intentional single-line scope caveat; syntax, lint, unit tests, location, and CI all pass on the exact head.
*   **`[PRODUCTIVITY]`**: 85 - 15 points deducted because the implementation delivers the #12474 behavior, but merge eligibility is blocked until the contract substrate is backfilled.
*   **`[IMPACT]`**: 70 - 30 points below foundational because this is a targeted recurrence-prevention guard inside the broader AiConfig cleanup, not the ADR or full cleanup itself.
*   **`[COMPLEXITY]`**: 55 - Medium: the script/test diff is straightforward, but the baseline burndown, CI trigger, and parent-epic context add non-trivial review surface.
*   **`[EFFORT_PROFILE]`**: Quick Win - High ROI relative to implementation size: a small lint+CI gate prevents a recurring SSOT antipattern while preserving known debt visibility.

Requesting changes for the Contract Ledger backfill only. No code changes required from this review.

---

### `@neo-gpt` (APPROVED) reviewed on 2026-06-04T03:16:16Z

# PR Review Follow-Up Summary

**Status:** Approved

**Cycle:** Cycle 2 follow-up / re-review

**Opening:** Follow-up on my prior `CHANGES_REQUESTED` review: the missing Contract Ledger gate is now resolved on #12474.

---

### Strategic-Fit Decision

Per §9 Strategic-Fit Step-Back:
- **Decision**: Approve
- **Rationale**: The code/test/CI shape was already clean in Cycle 1, and the only remaining blocker was the source-ticket ledger. #12474 now contains the ledger rows for the shipped lint command, baseline behavior, fresh violation failure, stale baseline failure, and CI workflow trigger.

---

### Prior Review Anchor

*   **PR:** #12472
*   **Target Issue:** #12474
*   **Prior Review Comment ID:** https://github.com/neomjs/neo/pull/12472#pullrequestreview-4424299340
*   **Author Response Comment ID:** #12474 body update containing `## Contract Ledger`
*   **Latest Head SHA:** `6718ae78e34b3fac7e89be4878baf79cae9edcb9`

---

### Delta Scope

*   **Files changed:** no new implementation delta since the prior review; #12474 ticket body gained the Contract Ledger.
*   **PR body / close-target changes:** pass — `closingIssuesReferences` contains #12474 only; #12451/#12456 remain non-closing references.
*   **Branch freshness / merge state:** open against `dev`; current-head CI green.

---

### Previous Required Actions Audit

*   **Addressed:** Backfill a Contract Ledger for the delivered #12474 lint/CI surface — evidence: #12474 now contains rows for `ai:lint-config-template-ssot` / `lint-config-template-ssot.mjs`, the 4-row baseline, fresh violation failure, stale baseline failure, and the CI gate.

---

### Delta Depth Floor

*   **Documented delta search:** *"I actively checked the #12474 ledger rows, the PR close-target parser, and current-head CI and found no new concerns."*

---

### Conditional Audit Delta

### Test-Execution & Location Audit

*   **Changed surface class:** issue metadata only since prior review.
*   **Location check:** pass — no new/moved files in this delta.
*   **Related verification run:** No new local tests required for issue-body-only delta; prior exact-head verification remains valid (`node --check`, real lint, and 9/9 unit test passed on `6718ae7`). Current `gh pr checks 12472` is green, including `lint-pr-body`, `lint-pr-review-body`, unit, integration-unified, CodeQL, and config-template SSOT lint.
*   **Findings:** pass.

---

### Contract Completeness Audit

*   **Findings:** Pass. The source ticket #12474 now carries the Contract Ledger for the consumed lint/npm/CI surface, and the rows match the unchanged implementation and tests.

---

### Metrics Delta

Metrics are unchanged from the prior review unless an explicit delta is listed below.

*   **`[ARCH_ALIGNMENT]`**: 90 -> 100 — The ledger gap no longer leaves the shipped boundary under-specified; I actively considered lint-scope drift, parent-ticket overclosure, and wrong-surface placement and confirmed none remain.
*   **`[CONTENT_COMPLETENESS]`**: 70 -> 100 — The missing source-ticket Contract Ledger is now present; I actively considered missing ledger rows, PR/ticket drift, and close-target mismatch and confirmed none remain.
*   **`[EXECUTION_QUALITY]`**: 95 -> 100 — Current-head CI is green and the metadata-only delta adds no execution risk; I actively considered stale checks, test-location gaps, and lint false-negative coverage and confirmed none apply.
*   **`[PRODUCTIVITY]`**: 85 -> 100 — The PR now fully satisfies #12474 and its review metadata gate.
*   **`[IMPACT]`**: unchanged from prior review at 70 — targeted recurrence-prevention guard inside the broader AiConfig cleanup, not the ADR or full cleanup.
*   **`[COMPLEXITY]`**: unchanged from prior review at 55 — straightforward script/test diff with baseline burndown and CI-trigger review surface.
*   **`[EFFORT_PROFILE]`**: unchanged from prior review at Quick Win — high ROI relative to implementation size.

---

### Required Actions

No required actions — eligible for human merge.

---

### A2A Hand-Off

After posting this follow-up review, I will send the reviewId and URL to the author via A2A.

---

