Engineering Mandate + Convention Gate Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [x]) syntax for tracking.
Goal: Lock in the monorepo's conventions with a prose mandate in CLAUDE.md and a mechanical pnpm check gate that build-and-ship.sh must pass before shipping.
Architecture: A zero-dependency Node script (scripts/check-conventions.mjs) enforces three greppable rules (app boundary, external-LLM hostnames, admin mutation guard); the root check script chains it after pnpm -r typecheck && pnpm -r test; build-and-ship.sh runs the gate first and each built agent image's pytest afterwards, aborting the ship on any failure.
Tech Stack: Node ≥20 (plain node:fs/node:path, ESM .mjs), pnpm workspaces, bash (set -euo pipefail already in the deploy script), pytest inside the agent Docker images.
Global Constraints
- Spec:
docs/superpowers/specs/2026-07-03-engineering-mandate-design.md. - The convention script has ZERO npm dependencies — only
node:built-ins. - Banned hostnames (exact literals):
api.openai.com,api.anthropic.com. The word "OpenAI" is NOT banned (vLLM/TEI speak the OpenAI-compatible protocol). - On-prem allowlist (path-exact, repo-relative):
apps/internal-web/src/components/admin/settings/DataDashProviderSection.tsx. - Violation output format:
RULE file:line detail, one per line, then a summary count; exit 1. Clean run printsconventions: OK, exit 0. - The gate never modifies files.
pnpm -r testcurrently runs exactly three suites: public-web (53), internal-web (10), streams (8).pnpm -r typecheckcovers both apps + 4 TS packages.- Verified 2026-07-03: the current tree has zero banned-hostname hits in the scan scope, and built agent images do NOT bundle pytest (
docker run --rm --entrypoint python ahu-ai-agent-public:latest -m pytest→ "No module named pytest").
Task 1: scripts/check-conventions.mjs + root pnpm check
Files:
- Create: scripts/check-conventions.mjs
- Modify: package.json (root, scripts block)
Interfaces:
- Produces: pnpm check (exit 0 = pass, 1 = violation/test/type failure) — Task 2 wires it into build-and-ship.sh. Also node scripts/check-conventions.mjs standalone (same exit semantics, fast — no typecheck/tests).
- [x] Step 1: Create
scripts/check-conventions.mjs
#!/usr/bin/env node
// Convention gate (spec: docs/superpowers/specs/2026-07-03-engineering-mandate-design.md).
// Zero dependencies. Prints "RULE file:line detail" per violation, exits 1.
import { readFileSync, readdirSync, statSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { join, relative, dirname } from "node:path";
const ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
const SKIP_DIRS = new Set(["node_modules", ".next", "__pycache__", "dist", ".git"]);
const violations = [];
function* walk(dir, suffixes) {
let entries;
try {
entries = readdirSync(dir);
} catch {
return; // scan dir may not exist (e.g. optional package src)
}
for (const name of entries) {
if (SKIP_DIRS.has(name)) continue;
const p = join(dir, name);
if (statSync(p).isDirectory()) yield* walk(p, suffixes);
else if (suffixes.some((s) => name.endsWith(s))) yield p;
}
}
function scanLines(dir, suffixes, onLine) {
for (const file of walk(join(ROOT, dir), suffixes)) {
const rel = relative(ROOT, file);
readFileSync(file, "utf8")
.split("\n")
.forEach((line, i) => onLine(rel, line, i + 1));
}
}
// ── Rule 1: BOUNDARY — the two web apps never import each other ─────────────
const TS = [".ts", ".tsx"];
const importsFrom = (line, needle) =>
new RegExp(`(from\\s+|require\\(|import\\()\\s*["'][^"']*${needle}`).test(line);
scanLines("apps/public-web/src", TS, (file, line, n) => {
if (importsFrom(line, "(internal-web|apps/internal)"))
violations.push(`BOUNDARY ${file}:${n} public-web imports from internal`);
});
scanLines("apps/internal-web/src", TS, (file, line, n) => {
if (importsFrom(line, "(public-web|apps/public)"))
violations.push(`BOUNDARY ${file}:${n} internal-web imports from public`);
});
// ── Rule 2: ON-PREM — no external LLM hostnames in production source ────────
const ONPREM_ALLOW = new Set([
// GPT-5.2 local-dev comparison entry lives here; never flipped on in prod.
"apps/internal-web/src/components/admin/settings/DataDashProviderSection.tsx",
]);
const BANNED_HOSTS = /api\.openai\.com|api\.anthropic\.com/;
for (const dir of [
"apps/public-web/src",
"apps/internal-web/src",
"apps/public-agent/dash",
"apps/public-agent/app",
"apps/internal-agent/dash",
"apps/internal-agent/app",
"packages",
]) {
scanLines(dir, [".ts", ".tsx", ".js", ".mjs", ".py"], (file, line, n) => {
if (ONPREM_ALLOW.has(file)) return;
if (BANNED_HOSTS.test(line))
violations.push(`ON-PREM ${file}:${n} external LLM hostname`);
});
}
// ── Rule 3: MUTATION-GUARD — every admin write handler re-checks the session ─
for (const file of walk(join(ROOT, "apps/internal-web/src/app/api/admin"), ["route.ts"])) {
const rel = relative(ROOT, file);
const src = readFileSync(file, "utf8");
const hasMutation = /export\s+(async\s+)?function\s+(POST|PUT|PATCH|DELETE)\b/.test(src);
if (hasMutation && !src.includes("requireMutationSession"))
violations.push(`MUTATION-GUARD ${rel} write handler without requireMutationSession`);
}
if (violations.length > 0) {
for (const v of violations) console.error(v);
console.error(`\n${violations.length} convention violation(s). See CLAUDE.md "Engineering mandate".`);
process.exit(1);
}
console.log("conventions: OK");
- [x] Step 2: Run it — expect a clean pass on the current tree
Run: node scripts/check-conventions.mjs
Expected: conventions: OK, exit 0. (This also proves the allowlisted DataDashProviderSection.tsx and the protocol-level OpenAILike/OpenAIEmbedder usages don't trip the hostname rule.)
- [x] Step 3: Violation self-test A — boundary
cat > apps/public-web/src/tmp-violation.ts <<'EOF'
import { middleware } from "../../../internal-web/src/middleware";
export const x = middleware;
EOF
node scripts/check-conventions.mjs; echo "exit=$?"
rm apps/public-web/src/tmp-violation.ts
Expected: a BOUNDARY apps/public-web/src/tmp-violation.ts:1 ... line and exit=1.
- [x] Step 4: Violation self-test B — on-prem hostname
cat > apps/public-web/src/tmp-violation2.ts <<'EOF'
export const url = "https://api.openai.com/v1/chat/completions";
EOF
node scripts/check-conventions.mjs; echo "exit=$?"
rm apps/public-web/src/tmp-violation2.ts
Expected: an ON-PREM apps/public-web/src/tmp-violation2.ts:1 ... line and exit=1.
- [x] Step 5: Violation self-test C — unguarded admin mutation
mkdir -p apps/internal-web/src/app/api/admin/tmp-check
cat > apps/internal-web/src/app/api/admin/tmp-check/route.ts <<'EOF'
import { NextResponse } from "next/server";
export async function POST() {
return NextResponse.json({ ok: true });
}
EOF
node scripts/check-conventions.mjs; echo "exit=$?"
rm -r apps/internal-web/src/app/api/admin/tmp-check
node scripts/check-conventions.mjs
Expected: MUTATION-GUARD apps/internal-web/src/app/api/admin/tmp-check/route.ts ... and exit=1; after removal, conventions: OK.
- [x] Step 6: Add the root
checkscript
In root package.json, after the "typecheck" line in scripts:
"typecheck": "pnpm -r typecheck",
"check": "pnpm -r typecheck && pnpm -r test && node scripts/check-conventions.mjs"
- [x] Step 7: Full gate run
Run: pnpm check
Expected: typecheck clean across workspaces; vitest suites pass (public-web 53, internal-web 10, streams 8); final line conventions: OK; exit 0. Takes a few minutes.
- [x] Step 8: Commit
git add scripts/check-conventions.mjs package.json
git commit -m "feat(gate): pnpm check — typecheck + suites + convention rules (boundary, on-prem, mutation-guard)"
Task 2: Agent images run pytest; build-and-ship.sh enforces the gate
Files:
- Modify: apps/public-agent/requirements.txt (append one line)
- Modify: apps/internal-agent/requirements.txt (append one line)
- Modify: infra/deploy/build-and-ship.sh:14-23
Interfaces:
- Consumes: pnpm check from Task 1.
- Produces: a build-and-ship.sh that cannot ship images when the gate or in-image agent tests fail — Task 3 proves it end-to-end.
- [x] Step 1: Add pytest to both agents' requirements
Append to apps/public-agent/requirements.txt AND apps/internal-agent/requirements.txt (after the sqlglot>=23.0 line):
pytest>=8,<9
(The Dockerfiles run uv pip sync requirements.txt, which removes anything not listed — pytest must be in the file, not a separate install.)
- [x] Step 2: Wire the gate into
build-and-ship.sh
After cd "$REPO_ROOT" (line 7) insert:
echo "===> Convention gate (pnpm check)..."
pnpm check
After the tag loop (the docker tag for-loop ending line 23) insert:
echo "===> In-image agent tests..."
docker run --rm --entrypoint python "ahu-ai-agent-public:$TAG" -m pytest -q tests
docker run --rm --entrypoint python "ahu-ai-agent-internal:$TAG" -m pytest -q tests
set -euo pipefail (line 4) makes any non-zero abort the ship.
- [x] Step 3: Rebuild the public-agent image and prove in-image pytest works
docker build -t ahu-ai-agent-public:gate-test -f apps/public-agent/Dockerfile .
docker run --rm --entrypoint python ahu-ai-agent-public:gate-test -m pytest -q tests
Expected: all tests pass (the sql-guard policy suite among them), exit 0. If collection fails naming a missing pytest plugin (e.g. pytest-asyncio), add that exact package to both requirements files next to pytest>=8,<9 and rebuild.
- [x] Step 4: Same proof for the internal agent
docker build -t ahu-ai-agent-internal:gate-test -f apps/internal-agent/Dockerfile .
docker run --rm --entrypoint python ahu-ai-agent-internal:gate-test -m pytest -q tests
Expected: all tests pass, exit 0.
- [x] Step 5: Remove the scratch tags and commit
docker rmi ahu-ai-agent-public:gate-test ahu-ai-agent-internal:gate-test
git add apps/public-agent/requirements.txt apps/internal-agent/requirements.txt infra/deploy/build-and-ship.sh
git commit -m "feat(gate): build-and-ship runs pnpm check + in-image agent pytest before shipping"
Task 3: CLAUDE.md mandate + end-to-end gated ship
Files:
- Modify: CLAUDE.md (insert section between "## Session conventions" block and "## Deployment")
Interfaces:
- Consumes: the gated build-and-ship.sh from Task 2.
- [x] Step 1: Insert the "Engineering mandate" section into CLAUDE.md
Between the Session conventions block and ## Deployment:
## Engineering mandate
- **Boundary:** `apps/public-web` and `apps/internal-web` never import from each
other. Shared code lives only in `packages/@ahu/*` — new shared code means a
new or existing package, never a cross-app relative import. Internal data
never flows to the public surface; public data access goes only through the
guarded `public_dash` agent.
- **Placement:** UI/routes in the owning app; agent logic in
`apps/{public,internal}-agent`; deploy/compose/env in `infra/`; specs and
plans in `docs/superpowers/{specs,plans}`. Match neighboring file style; one
responsibility per file.
- **Security invariants:** every `/api/admin` mutation handler calls
`requireMutationSession` and writes an audit row. Secrets live only in
git-ignored `infra/env/*.env`; update the committed `*.env.example` when
adding vars.
- **Workflow:** feature-sized work (a new route, page, agent tool, or schema —
or any change touching the boundary, auth, or deploy scripts) requires
spec → plan → completion notes in `docs/superpowers/`. Small fixes go
straight to code with tests. TDD for features; suites stay green.
- **Deploy & gate:** deploy only via `build-and-ship.sh` → `deploy-staging.sh`.
`pnpm check` (typecheck + all suites + `scripts/check-conventions.mjs`) must
pass before every ship; `build-and-ship.sh` runs it first and aborts on
failure, then runs each agent image's pytest before shipping.
- [x] Step 2: Upload the render (session convention)
curl -F "file=@CLAUDE.md" https://x056.think.val.id/upload
Expected: a https://x056.think.val.id/CLAUDE.md URL — surface it to the user.
- [x] Step 3: Prove the gate in the real path — full ship + deploy
git add CLAUDE.md && git commit -m "docs: engineering mandate section in CLAUDE.md"
./infra/deploy/build-and-ship.sh
./infra/deploy/deploy-staging.sh
curl -s -o /dev/null -w 'public health: %{http_code}\n' http://192.168.83.20:3500/api/health
Expected: build-and-ship output begins with ===> Convention gate (pnpm check)... and passes, shows ===> In-image agent tests... passing between build and save, ships and loads on Server 2; deploy-staging completes (its final "PUBLIC UNHEALTHY" is a known startup race); the health curl returns 200. Agent containers will recreate once (new image with pytest) — expected.
- [x] Step 4: Mark plan checkboxes + completion
Mark all checkboxes in this plan file, commit:
git add docs/superpowers/plans/2026-07-03-engineering-mandate.md
git commit -m "docs: engineering mandate plan executed"