azcodr 2.4.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Cross-platform architectural boundary & cycle guard.
5
+ *
6
+ * Ships inside scaffolded projects so structural drift (circular imports and
7
+ * Clean/Hexagonal layer breaches) is machine-checked with zero third-party
8
+ * dependencies. The engine is vendored under `.agents/lib/` and byte-locked to
9
+ * `lib/boundaries.js`; this wrapper only resolves it and reports the verdict.
10
+ */
11
+ import fs from 'node:fs';
12
+ import path from 'node:path';
13
+ import process from 'node:process';
14
+ import { fileURLToPath, pathToFileURL } from 'node:url';
15
+
16
+ const selfDir = path.dirname(fileURLToPath(import.meta.url));
17
+
18
+ // Resolution order (first existing file wins):
19
+ // 1. AZCODR_BOUNDARY_ENGINE override (tests, embedders).
20
+ // 2. ../lib/boundaries.js — vendored engine inside `.agents/`
21
+ // (present in scaffolded projects; `.agents` is copied recursively).
22
+ // 3. ../../lib/boundaries.js — azcodr repo dev layout (fallback only).
23
+ const engineCandidates = [
24
+ process.env.AZCODR_BOUNDARY_ENGINE,
25
+ path.resolve(selfDir, '../lib/boundaries.js'),
26
+ path.resolve(selfDir, '../../lib/boundaries.js')
27
+ ].filter(Boolean);
28
+
29
+ async function getEngine() {
30
+ const tried = [];
31
+ for (const candidate of engineCandidates) {
32
+ if (!fs.existsSync(candidate)) {
33
+ tried.push(candidate);
34
+ continue;
35
+ }
36
+ try {
37
+ return await import(pathToFileURL(candidate).href);
38
+ } catch (err) {
39
+ tried.push(`${candidate} (unreadable: ${err.message})`);
40
+ }
41
+ }
42
+ // Fail CLOSED: a guard with no engine is a broken install, never a silent pass.
43
+ console.error(
44
+ '🚨 Boundary Guard misconfigured: engine module not found, refusing to fail open. Tried:\n' +
45
+ tried.map((t) => ` - ${t}`).join('\n')
46
+ );
47
+ process.exit(2);
48
+ }
49
+
50
+ function resolveTarget(positional) {
51
+ const explicit = positional[0];
52
+ if (explicit) return path.resolve(explicit);
53
+ const srcDir = path.resolve(process.cwd(), 'src');
54
+ return fs.existsSync(srcDir) ? srcDir : process.cwd();
55
+ }
56
+
57
+ function rel(p) {
58
+ return path.relative(process.cwd(), p) || '.';
59
+ }
60
+
61
+ function printReport(report) {
62
+ console.log('🔍 Inspecting Architectural Boundaries in: ' + rel(report.targetDir));
63
+ console.log('--------------------------------------------------------------');
64
+ if (report.ok) {
65
+ console.log(`✅ No cycles or boundary violations across ${report.moduleCount} module(s).`);
66
+ return;
67
+ }
68
+ for (const cycle of report.cycles) {
69
+ console.error(' - Circular Dependency: ' + cycle.map(rel).join(' -> '));
70
+ }
71
+ for (const v of report.violations) {
72
+ console.error(` - Layer Boundary Breach: ${rel(v.file)} imports ${rel(v.importedFile)} (${v.fromLayer} -> ${v.toLayer})`);
73
+ }
74
+ }
75
+
76
+ function emit(report, silent) {
77
+ if (!silent) printReport(report);
78
+ }
79
+
80
+ async function main() {
81
+ const args = process.argv.slice(2).filter((a) => a !== '--silent');
82
+ const silent = process.argv.includes('--silent');
83
+ const { inspectBoundaries } = await getEngine();
84
+ const target = resolveTarget(args);
85
+ const report = inspectBoundaries(target);
86
+ emit({ ...report, targetDir: target }, silent);
87
+ process.exit(report.ok ? 0 : 1);
88
+ }
89
+
90
+ main().catch((err) => {
91
+ console.error(`🚨 Boundary Guard crashed: ${err?.message || err}`);
92
+ process.exit(2);
93
+ });
@@ -51,6 +51,7 @@ Target concrete flaws:
51
51
  ### Step 5: Verify Continuous Green State
52
52
  - Run tests after every single atomic change. Use the workspace's own command — do not assume one exists. Verify with the package manifest first (`node -p "JSON.stringify(require('./package.json').scripts)"`); for this template that is `npm test`, with `npm run test:coverage` for the coverage gate.
53
53
  - Ensure coverage remains at **100.00%**.
54
+ - Verify architectural boundaries: Run `npx azcodr boundaries` to guarantee the refactoring introduced 0 circular dependencies and 0 layer boundary breaches.
54
55
 
55
56
  ---
56
57
 
@@ -100,7 +100,7 @@ Upon user confirmation:
100
100
  - *Extension:* `manifest.json`, `src/background/index.ts`, `src/content/index.ts`, `src/popup/index.html`.
101
101
  - *Game / Engine:* `src/core/`, `src/ecs/`, asset manifest, frame loop entrypoint.
102
102
  - *CLI:* `src/cmd/`, `src/core/`, CLI entrypoint with exit code handling.
103
- 3. Generate build manifests (`Cargo.toml`, `package.json`, `go.mod`, `pyproject.toml`), deterministic linter configurations strictly enforcing the [Polyglot Fitness Function Standards](../../../docs/rules/clean_code.md#polyglot-fitness-function-standards), and boundary smoke test (`scripts/smoke_test.sh`).
103
+ 3. Generate build manifests (`Cargo.toml`, `package.json`, `go.mod`, `pyproject.toml`) for the recorded language, install the pinned linter from the emitted gate configuration (`eslint.config.js`, `ruff.toml`, `clippy.toml`, `.golangci.yml`, `checkstyle.xml`, `.editorconfig` CA block, `.clang-tidy` — written deterministically by `bootstrap_workspace.sh`, never invented). Node profiles arrive with `package.json` lint already rewired to `eslint .`; other profiles need the lint entry wired by hand. Prove the gate in Phase 5, and keep the boundary smoke test (`scripts/smoke_test.sh`).
104
104
  4. **Replace Starter README with Project-Specific README**:
105
105
  Generate a clean, project-specific `README.md` using [references/project_readme_template.md](./references/project_readme_template.md), completely replacing meta-template content with the project's actual name, mission, stack highlights, quickstart commands, and directory tree.
106
106
 
@@ -114,6 +114,7 @@ Upon user confirmation:
114
114
  2. Execute toolchain dependency checks, build commands, and health/smoke tests:
115
115
  - Compile code and verify zero compiler or lint errors.
116
116
  - Verify boundary verification smoke test (`scripts/smoke_test.sh`).
117
+ - **Prove enforcement is live:** enable the shipped hooks in `.agents/hooks.json` (`safety-guard`, `architectural-guard`), then deliberately attempt a blocked action (e.g. append lines to a file already over budget, or a denied command) and confirm the hook rejects it before re-disabling nothing — hooks stay enabled. A guard that has never blocked anything is a rumor, not a control.
117
118
  3. **Mandatory Handover to Domain Analysis (STOP & PIVOT):**
118
119
  - **`lets-build` IS NOW COMPLETE.** Do NOT proceed to write domain business entities, repositories, or application features.
119
120
  - Present the bootstrapped technical skeleton to the user.
@@ -160,7 +161,7 @@ Upon user confirmation:
160
161
 
161
162
  ## 4. Quality & Verification
162
163
  - **Testing Strategy:** Outside-In TDD with Nano-Cycles (Uncle Bob's 3 Laws)
163
- - **Code Health Gates:** 100.00% test coverage gate, zero lint errors
164
+ - **Code Health Gates:** 100% coverage on all metrics + mutation kill gate on guards, zero lint errors
164
165
  - **DevSecOps:** <Semgrep / Trivy / Gitleaks / None>
165
166
  ```
166
167
 
@@ -324,6 +324,165 @@ EOF
324
324
  ;;
325
325
  esac
326
326
 
327
+ # ------------------------------------------------------------------------------
328
+ # DETERMINISTIC TOOLCHAIN FILES. Directories alone enforce nothing: until this
329
+ # section existed, every language produced zero gate configurations and the
330
+ # starter `lint` script was an echo placeholder, so all enforcement was
331
+ # agent-authored. Each profile below emits its pinned gate configuration with
332
+ # values mirroring docs/rules/clean_code.md (300/30/10/3); the agent installs
333
+ # the named tool and proves the gate in Phase 5. Files are created only when
334
+ # absent, so re-runs never clobber agent-authored configs. Build manifests
335
+ # (Cargo.toml, go.mod, csproj, ...) stay with the agent: they carry project
336
+ # naming and version decisions no script may invent.
337
+ # ------------------------------------------------------------------------------
338
+ write_unless_exists() {
339
+ local target="$1"
340
+ if [[ -f "${target}" ]]; then
341
+ echo " keeping existing ${target}"
342
+ return
343
+ fi
344
+ mkdir -p "$(dirname "${target}")"
345
+ cat > "${target}"
346
+ }
347
+
348
+ append_unless_present() {
349
+ local target="$1"
350
+ local marker="$2"
351
+ if [[ -f "${target}" ]] && grep -qF "${marker}" "${target}"; then
352
+ echo " keeping existing ${target} gates"
353
+ return
354
+ fi
355
+ cat >> "${target}"
356
+ }
357
+
358
+ echo "6. Emitting deterministic toolchain configs for '${LANGUAGE}'..."
359
+ case "${LANGUAGE}" in
360
+ typescript|javascript|deno|bun)
361
+ write_unless_exists "${WORKSPACE_ROOT}/eslint.config.js" << 'EOF'
362
+ // Deterministic fitness functions (azcodr clean_code.md section 5).
363
+ // Install the pinned tool, then prove the gate: npm run lint
364
+ export default [
365
+ {
366
+ files: ['src/**/*.{js,ts}', 'tests/**/*.{js,ts}'],
367
+ rules: {
368
+ 'max-lines': ['error', 300],
369
+ 'max-lines-per-function': ['error', 30],
370
+ complexity: ['error', 10],
371
+ 'max-params': ['error', 3]
372
+ }
373
+ }
374
+ ];
375
+ EOF
376
+ ;;
377
+ python)
378
+ write_unless_exists "${WORKSPACE_ROOT}/ruff.toml" << 'EOF'
379
+ # Deterministic fitness functions (azcodr clean_code.md section 5).
380
+ # Install the pinned tool, then prove the gate: ruff check .
381
+ [lint]
382
+ select = ["E", "F", "C901", "PLR0912", "PLR0913", "PLR0915"]
383
+ [lint.mccabe]
384
+ max-complexity = 10
385
+ [lint.pylint]
386
+ max-args = 3
387
+ max-statements = 30
388
+ # NOTE: ruff has no file-length rule; the 300-line file cap is enforced by
389
+ # the project's lint entry (see lets-build Phase 5 proof).
390
+ EOF
391
+ ;;
392
+ rust)
393
+ write_unless_exists "${WORKSPACE_ROOT}/clippy.toml" << 'EOF'
394
+ # Deterministic fitness functions (azcodr clean_code.md section 5).
395
+ # Enforce with: cargo clippy -- -D clippy::too_many_lines -D clippy::cognitive_complexity -D clippy::too_many_arguments
396
+ too-many-lines-threshold = 30
397
+ cognitive-complexity-threshold = 10
398
+ too-many-arguments-threshold = 3
399
+ # NOTE: clippy has no file-length lint; the 300-line file cap is enforced by
400
+ # the project's lint entry (see lets-build Phase 5 proof).
401
+ EOF
402
+ ;;
403
+ go)
404
+ write_unless_exists "${WORKSPACE_ROOT}/.golangci.yml" << 'EOF'
405
+ # Deterministic fitness functions (azcodr clean_code.md section 5).
406
+ # Install the pinned tool, then prove the gate: golangci-lint run ./...
407
+ linters:
408
+ enable: [funlen, gocyclo]
409
+ linters-settings:
410
+ funlen:
411
+ lines: 30
412
+ statements: 25
413
+ gocyclo:
414
+ min-complexity: 10
415
+ # NOTE: no golangci-native file-length check; the 300-line file cap is
416
+ # enforced by the project's lint entry (see lets-build Phase 5 proof).
417
+ EOF
418
+ ;;
419
+ java)
420
+ write_unless_exists "${WORKSPACE_ROOT}/checkstyle.xml" << 'EOF'
421
+ <?xml version="1.0"?>
422
+ <!-- Deterministic fitness functions (azcodr clean_code.md section 5). -->
423
+ <!DOCTYPE module PUBLIC "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN" "https://checkstyle.org/dtds/configuration_1_3.dtd">
424
+ <module name="Checker">
425
+ <module name="FileLength">
426
+ <property name="max" value="300"/>
427
+ </module>
428
+ <module name="TreeWalker">
429
+ <module name="MethodLength">
430
+ <property name="max" value="30"/>
431
+ </module>
432
+ <module name="CyclomaticComplexity">
433
+ <property name="max" value="10"/>
434
+ </module>
435
+ <module name="ParameterNumber">
436
+ <property name="max" value="3"/>
437
+ </module>
438
+ </module>
439
+ </module>
440
+ EOF
441
+ ;;
442
+ csharp)
443
+ append_unless_present "${WORKSPACE_ROOT}/.editorconfig" "azcodr fitness functions" << 'EOF'
444
+
445
+ # --- azcodr fitness functions (CA gates; exact numbers in lint entry) ---
446
+ [*.cs]
447
+ dotnet_diagnostic.CA1501.severity = error
448
+ dotnet_diagnostic.CA1502.severity = error
449
+ EOF
450
+ ;;
451
+ cpp|c)
452
+ write_unless_exists "${WORKSPACE_ROOT}/.clang-tidy" << 'EOF'
453
+ # Deterministic fitness functions (azcodr clean_code.md section 5).
454
+ Checks: 'readability-function-size,readability-function-cognitive-complexity'
455
+ CheckOptions:
456
+ - { key: readability-function-size.LineThreshold, value: 30 }
457
+ - { key: readability-function-size.ParameterThreshold, value: 3 }
458
+ - { key: readability-function-cognitive-complexity.Threshold, value: 10 }
459
+ # NOTE: clang-tidy has no file-length check; the 300-line file cap is
460
+ # enforced by the project's lint entry (see lets-build Phase 5 proof).
461
+ EOF
462
+ ;;
463
+ generic)
464
+ echo " language undecided: no toolchain configs emitted (re-run with a language profile)"
465
+ ;;
466
+ esac
467
+
468
+ # For Node profiles the starter package.json ships a lint placeholder that
469
+ # always succeeds. Point it at the emitted config so `npm run lint` fails
470
+ # until the pinned eslint is installed (fail-closed) instead of echoing.
471
+ # Only the placeholder is ever replaced; agent-wired entries are sacred.
472
+ case "${LANGUAGE}" in
473
+ typescript|javascript|deno|bun)
474
+ STARTER_PKG="${WORKSPACE_ROOT}/package.json"
475
+ if [[ -f "${STARTER_PKG}" ]] && grep -q 'No linter configured yet' "${STARTER_PKG}"; then
476
+ if command -v node >/dev/null 2>&1; then
477
+ node -e 'const fs=require("fs");const p=process.argv[1];const j=JSON.parse(fs.readFileSync(p,"utf8"));j.scripts=j.scripts||{};j.scripts.lint="eslint .";fs.writeFileSync(p,JSON.stringify(j,null,2)+"\n");' "${STARTER_PKG}"
478
+ echo " wired package.json lint -> eslint ."
479
+ else
480
+ echo " node not found: leaving starter lint placeholder (agent wires it in Phase 4)"
481
+ fi
482
+ fi
483
+ ;;
484
+ esac
485
+
327
486
  # ------------------------------------------------------------------------------
328
487
  # memory.md is an APPEND-ONLY LEDGER. It is never rewritten automatically.
329
488
  #
@@ -408,7 +567,14 @@ if [[ ! -f "${SMOKE_TEST}" ]]; then
408
567
  # ==============================================================================
409
568
  set -euo pipefail
410
569
 
570
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
571
+
411
572
  echo "Running boundary smoke verification..."
573
+ if [[ -f "${ROOT}/.agents/scripts/boundary_guard.js" ]] && command -v node >/dev/null 2>&1; then
574
+ node "${ROOT}/.agents/scripts/boundary_guard.js" "${ROOT}/src"
575
+ else
576
+ echo "⚠️ Boundary guard skipped (node or guard script unavailable)."
577
+ fi
412
578
  # Extend with project-specific runtime health checks (e.g. ping health endpoint, CLI --help)
413
579
  echo "✅ Boundary smoke verification passed!"
414
580
  EOF
@@ -119,7 +119,7 @@ Synthesize the answers into an unambiguous **Feature Alignment Specification (FA
119
119
  When [Action]
120
120
  Then [Observable Outcome]
121
121
  ```
122
- - **Test Strategy:** [Contract / Integration / Unit tests required for 100% coverage]
122
+ - **Test Strategy:** [Contract / Integration / Unit tests required; 100% on all metrics, assertion-free tests rejected]
123
123
  ```
124
124
 
125
125
  ---
package/AGENTS.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # AGENTS.md
2
2
 
3
- > **azcodr: Enterprise Architecture & Agentic Engineering Starter Template**
3
+ > **azcodr: Architecture governance toolkit, with an optional starter**
4
4
  > **Workspace Mission:** Problem-first, topology-aligned production architectures governed by strict systemic atomicity, 100% open-source standards, true incremental TDD nano-cycles, and zero speculative bloat.
5
5
  > **Runtime & Tools:** Node.js (`>=22.8.0`), npm (`>=10.0.0`) | `npm test` (test runner), `npm run test:coverage` (coverage gate), `npm run lint`, `npm run validate`.
6
6
  > **Node floor rationale:** `>=22.8.0` satisfies Jest native ESM requirements (`--experimental-vm-modules`) and modern LTS security baselines. Node 18 (EOL 2025-04-30) and 20 (EOL 2026-04-30) no longer receive security patches.
@@ -48,7 +48,7 @@ Progress all tasks systematically through the unified **Agent Cognitive & Agile
48
48
  2. INTERROGATE / DOMAIN ──► Relentless questioning; Ubiquitous Language, Aggregate invariants & state machines.
49
49
  3. PLAN / OUTER TDD ──► Minimal blast radius; failing Outer Acceptance Test (UI/API RED).
50
50
  4. EXECUTE / INNER TDD ──► Incremental nano-cycles (Uncle Bob's 3 Laws: 1 micro-assertion RED ➔ MINIMAL pass GREEN ➔ REFACTOR).
51
- 5. VERIFY / DoD & PROOF ──► Outer test turns GREEN; boundary smoke tests & 100.00% test coverage.
51
+ 5. VERIFY / DoD & PROOF ──► Outer test turns GREEN; boundary smoke tests & 100% coverage on all metrics + mutation gate.
52
52
  ```
53
53
  ---
54
54
 
@@ -58,7 +58,7 @@ To prevent context bloat and keep prompt overhead minimal, detailed engineering
58
58
 
59
59
  | Domain | Rule Reference File | When to Consult |
60
60
  |---|---|---|
61
- | **TDD & Isolation** | [docs/rules/test_driven_development.md](./docs/rules/test_driven_development.md) | Outside-In TDD, Uncle Bob's 3 Laws, 100% coverage, test isolation & DB rollback. |
61
+ | **TDD & Isolation** | [docs/rules/test_driven_development.md](./docs/rules/test_driven_development.md) | Outside-In TDD, Uncle Bob's 3 Laws, 100% coverage on all metrics + mutation gate, test isolation & DB rollback. |
62
62
  | **Clean Code** | [docs/rules/clean_code.md](./docs/rules/clean_code.md) | Naming, small functions, CQS, SLAP, DRY, DbC, zero side-effects. |
63
63
  | **Design Patterns** | [docs/rules/design_patterns.md](./docs/rules/design_patterns.md) | Adapter, Factory, Strategy, Result `<T, E>`, and GoF pattern catalog. |
64
64
  | **Type Safety** | [docs/rules/type_safety.md](./docs/rules/type_safety.md) | Compiler strictness, branded nominal types, type discriminators across polyglot languages. |
package/CONTRIBUTING.md CHANGED
@@ -49,15 +49,21 @@ Before submitting any code changes, all 5 quality gates must pass with zero erro
49
49
  npm test
50
50
  npm run test:coverage
51
51
  ```
52
- *Coverage gates enforced:*
52
+ *Coverage gates enforced (CI fails below 100% on any metric):*
53
53
  - **100% lines coverage**
54
- - **97% functions coverage**
55
- - **98% branches coverage**
54
+ - **100% functions coverage**
55
+ - **100% branches coverage**
56
+ - **100% statements coverage**
57
+ - Plus **100% mutant-kill on guards** (`npm run test:mutation`) and rejection of assertion-free tests.
56
58
  5. **Agentic Architecture Validation:**
57
59
  ```bash
58
60
  npm run validate
59
61
  ```
60
- Validates root harness parity, progressive disclosure rules, skill formats, markdown links, and ADR ledger consistency.
62
+ Validates root harness parity, progressive disclosure rules, skill formats, markdown links, ADR ledger consistency, and toolchain gate presence.
63
+ 6. **Vendored Engine Sync:** after any change under `src/agent-guard*.ts` or `src/boundaries.ts` and `npm run build`, re-copy the compiled engine files into `.agents/lib/` (byte-equality is enforced by `tests/agent-guard.test.ts` and `tests/boundary-guard.test.ts`):
64
+ ```bash
65
+ node -e "const fs=require('fs');for(const f of['agent-guard.js','agent-guard-command.js','agent-guard-file.js','agent-guard-tdd.js','boundaries.js'])fs.copyFileSync('lib/'+f,'.agents/lib/'+f)"
66
+ ```
61
67
 
62
68
  ---
63
69
 
package/README.md CHANGED
@@ -1,13 +1,15 @@
1
- # azcodr: Enterprise Architecture & Agentic Engineering Starter Template
1
+ # azcodr: Architecture governance toolkit, with an optional starter
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/azcodr.svg?color=cb3837)](https://www.npmjs.com/package/azcodr)
4
4
  [![JSR Package](https://jsr.io/badges/@azcodr/azcodr)](https://jsr.io/@azcodr/azcodr)
5
5
  [![CI Status](https://github.com/prosubodh/azcodr/actions/workflows/ci.yml/badge.svg)](https://github.com/prosubodh/azcodr/actions/workflows/ci.yml)
6
- [![Coverage](https://img.shields.io/badge/coverage-100.00%25-brightgreen.svg)](https://github.com/prosubodh/azcodr)
7
- [![Mutation Score](https://img.shields.io/badge/mutants%20killed-100%25-brightgreen.svg)](https://github.com/prosubodh/azcodr)
6
+ [![Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen.svg)](https://github.com/prosubodh/azcodr/actions/workflows/ci.yml)
7
+ [![Mutation Score](https://img.shields.io/badge/mutants%20killed-100%25-brightgreen.svg)](https://github.com/prosubodh/azcodr/blob/main/benchmark/RESULTS.md)
8
8
  [![Dependencies](https://img.shields.io/badge/dependencies-0-success.svg)](https://www.npmjs.com/package/azcodr)
9
9
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
10
10
 
11
+ > CI-enforced 100% gate (lines / branches / functions / statements) + 100% mutant kill on guards; the public report is the `coverage-report` artifact on every CI run.
12
+
11
13
  > **Production-oriented architecture governance and topology-scoped scaffolding for AI-assisted development (early release).**
12
14
  > *AI agents move fast. Architecture drifts. Azcodr turns architectural intent into executable constraints your agent cannot skip.*
13
15
 
@@ -34,7 +36,7 @@
34
36
 
35
37
  ## ⚡ 60-Second Demo: See Drift Prevention in Action
36
38
 
37
- Azcodr turns architectural philosophy into machine-checked constraints your agent cannot skip.
39
+ Azcodr turns architectural philosophy into machine-checked constraints. In a fresh scaffold the runtime hooks ship **enabled by default**, and the boundary engine runs in CI, so drift is caught both while the agent edits and after it commits.
38
40
 
39
41
  ### 1. Standalone Architectural Check
40
42
  Run the validator on any repository without scaffolding:
@@ -63,7 +65,7 @@ When an AI coding agent attempts to dump new logic into a file already exceeding
63
65
  ```
64
66
 
65
67
  ### 3. Zero-Dependency Boundary & Cycle Enforcement
66
- Inspect dependency cycles and layer boundaries on demand without third-party linters:
68
+ Inspect dependency cycles and layer boundaries on demand without third-party linters. The same engine is vendored into every scaffolded project (`.agents/scripts/boundary_guard.js`) and runs in the generated CI:
67
69
  ```bash
68
70
  npx azcodr boundaries [directory]
69
71
  ```
@@ -76,17 +78,17 @@ npx azcodr boundaries [directory]
76
78
  Action Required: Invert dependencies via Ports or extract shared modules.
77
79
  ```
78
80
 
79
- ### 4. Empirical Benchmark Proof (3 Arms, 10 Sequential Tickets)
80
- In an automated empirical trial across 10 sequential tickets with 3 architectural traps ([`benchmark/RESULTS.md`](https://github.com/prosubodh/azcodr/blob/main/benchmark/RESULTS.md)):
81
+ ### 4. Detector Validation on Synthetic Fixtures (Not a Live-Agent Trial)
82
+ The evaluator's ability to detect the 3 trap classes is exercised against **hand-written fixtures**, not live agent output ([`benchmark/RESULTS.md`](https://github.com/prosubodh/azcodr/blob/main/benchmark/RESULTS.md)):
81
83
 
82
84
  | Metric | Arm A (Control) | Arm B (Hooks Only) | Arm C (Full Azcodr) |
83
85
  |---|:---:|:---:|:---:|
84
86
  | **Files >300 Lines** | 1 (365 lines in `auth.ts`) | **0** (Modularized) | **0** (Modularized) |
85
87
  | **Circular Dependency Cycles** | 1 (`billing <-> tenant`) | 1 (`billing <-> tenant`) | **0** (Decoupled Port) |
86
88
  | **Layer Boundary Breaches** | 1 (`domain -> infra`) | 1 (`domain -> infra`) | **0** (Hexagonal Port) |
87
- | **Overall Drift Reduction** | ❌ FAILED | ❌ FAILED | ✅ **100% DRIFT-FREE** |
89
+ | **Detected drift-free** | ❌ FAILED | ❌ FAILED | ✅ **0 detector violations** |
88
90
 
89
- Run the empirical benchmark yourself:
91
+ These fixtures are committed by hand to prove the detector fires; they are **not evidence** that an AI agent drifts. Real-agent results (tokens, completion, review minutes) are planned and not yet measured. Run the detector yourself:
90
92
  ```bash
91
93
  node benchmark/run-benchmark.js
92
94
  ```
@@ -127,7 +129,7 @@ The architecture enforces 28 cohesive, single-responsibility domain rules. Read
127
129
 
128
130
  | Domain | Rule Reference File | Key Focus & Invariants |
129
131
  |---|---|---|
130
- | **TDD & Isolation** | [`test_driven_development.md`](./docs/rules/test_driven_development.md) | Outside-In TDD, Uncle Bob's 3 Laws, 100% coverage, test isolation & DB rollback. |
132
+ | **TDD & Isolation** | [`test_driven_development.md`](./docs/rules/test_driven_development.md) | Outside-In TDD, Uncle Bob's 3 Laws, 100% coverage on all metrics + mutation gate, test isolation & DB rollback. |
131
133
  | **Clean Code** | [`clean_code.md`](./docs/rules/clean_code.md) | Naming, small functions, CQS, SLAP, DRY, DbC, zero side-effects. |
132
134
  | **Design Patterns** | [`design_patterns.md`](./docs/rules/design_patterns.md) | Adapter, Factory, Strategy, Result `<T, E>`, and GoF pattern catalog. |
133
135
  | **Type Safety** | [`type_safety.md`](./docs/rules/type_safety.md) | Compiler strictness, branded nominal types, type discriminators across polyglot languages. |
@@ -171,10 +173,10 @@ The architecture enforces 28 cohesive, single-responsibility domain rules. Read
171
173
 
172
174
  ## 🚀 Starting a New Project with `/lets-build`
173
175
 
174
- This repository serves as an **enterprise architectural starter template**. When beginning a new software project:
176
+ This repository serves as an **architecture governance toolkit, with an optional starter**. When beginning a new software project:
175
177
 
176
178
  ### Step 1: Initialize Workspace with npx
177
- Pull and scaffold the complete enterprise architectural template into your project directory using `npx`:
179
+ Pull and scaffold the complete architecture governance toolkit into your project directory using `npx`:
178
180
  ```bash
179
181
  npx azcodr my-new-project
180
182
  cd my-new-project
@@ -0,0 +1,162 @@
1
+ # Generated-Project Enforcement Analysis: What a Scaffolded Project Actually Gets
2
+
3
+ > **Status:** evidence, not prose. Every claim below was produced by running the
4
+ > scaffolder on 2026-10-08, not by reading the skills docs.
5
+ > **Answers the plan's open question:** which linters does a non-TypeScript
6
+ > project actually get — and whether the "executable architecture" promise holds
7
+ > outside azcodr's own repo.
8
+
9
+ ---
10
+
11
+ ## Method
12
+
13
+ 1. Ran `scaffold({ force: true, noGit: true })` from compiled `lib/` into a clean
14
+ temp dir; inventoried every emitted file.
15
+ 2. Ran `bootstrap_workspace.sh . <topology> <language>` under Git Bash for
16
+ `backend+typescript`, `backend+python`, `cli+go`, `extension+typescript`,
17
+ `systems-library+c`; diffed the file lists.
18
+ 3. Executed the scaffolded project's `.agents/scripts/agent_guard.js` with a
19
+ must-block payload (`rm -rf /`), with the azcodr repo itself as control.
20
+
21
+ ## Finding 1: No deterministic linter in any generated project
22
+
23
+ The scaffolded project contains exactly one toolchain-adjacent file:
24
+ `.editorconfig`. Its `package.json` lint script is:
25
+
26
+ ```json
27
+ "lint": "echo \"No linter configured yet. Run /lets-build to configure toolchain.\""
28
+ ```
29
+
30
+ The polyglot fitness-function table (`docs/rules/clean_code.md`, § Polyglot
31
+ Fitness Function Standards: ESLint / ruff / clippy / golangci-lint /
32
+ Checkstyle / Roslyn) ships as **documentation only**. Phase 4 step 3 of
33
+ `lets-build/SKILL.md` instructs the *agent* to "generate … deterministic
34
+ linter configurations" — i.e. enforcement in a user's project is agent-authored
35
+ and non-deterministic, exactly the gap the plan flagged. **A non-TypeScript
36
+ project gets no linter at all unless the agent writes one.**
37
+
38
+ ## Finding 2: LANGUAGE does not change generated output
39
+
40
+ `backend+typescript` and `backend+python` produce byte-identical file lists
41
+ (18 files each: `.gitkeep` dirs, `tokens.json`, `smoke_test.sh`,
42
+ `workspace-profile.env`). `LANGUAGE` is validated and recorded but never
43
+ branches generation. Topology *does* scope directories (7–18 files by
44
+ topology). So: topology-scoping is deterministic; language support is a
45
+ recorded intention, not a generated artifact.
46
+
47
+ ## Finding 3: The shipped runtime guard is inert in generated projects (fail-open)
48
+
49
+ `TEMPLATE_ITEMS` (`src/scaffold.ts`) ships `docs`, `.agents`, `scripts`,
50
+ `.github` — but **not** `src/`, `lib/`, or `bin/`. Consequences, all verified:
51
+
52
+ | Check | azcodr repo (control) | Scaffolded project |
53
+ |---|---|---|
54
+ | `.agents/scripts/agent_guard.js` vs `rm -rf /` payload | **BLOCKED** (exit 1) | **ALLOWED** (exit 0) |
55
+ | `lib/agent-guard.js` present | yes | **missing** — the guard's dynamic import rejects, and `main().catch(() => process.exit(0))` fails open |
56
+ | `.agents/hooks.json` references `agent_guard.js` | no | no |
57
+ | All hook blocks in `hooks.json` | `enabled: false` | `enabled: false` (shipped copy) |
58
+
59
+ The flagship "agent cannot skip" control therefore degrades, in a generated
60
+ project, to: an unwired hook script, with its engine missing, configured off,
61
+ that permits on error. The `boundaries` engine (`npx azcodr boundaries`)
62
+ likewise does not ship — only `scripts/validate-cli.js` (config/ledger/link
63
+ checks) is present.
64
+
65
+ ## What *does* transfer deterministically
66
+
67
+ - 28 rule docs, 6 skills, `AGENTS.md` parity pointers, empty ADR ledger
68
+ (`data/memory.template`), `safety_guard.sh` + `verify_completion.sh` (present
69
+ but disabled in `hooks.json`), `smoke_test.sh` placeholder, topology-scoped
70
+ directories, `tokens.json` (backend/web only), `.azcodr/workspace-profile.env`.
71
+
72
+ ## Recommendations (product decisions, not taken here)
73
+
74
+ 1. **Either ship the engine or stop implying it ships.** Options: (a) add a
75
+ dependency on the published `azcodr` package + `lib/`- backed guard entry in
76
+ the starter `package.json`; (b) vendor a self-contained guard bundle into
77
+ `.agents/scripts/` with no `../../lib` import; (c) reword SKILL.md Phase 4
78
+ step 3 from "generate deterministic linter configurations" to an explicit
79
+ agent-authored checklist with verification (`npm run lint` must fail before
80
+ configs land, not echo).
81
+ 2. **Fail closed, and wire the hook.** `agent_guard.js`'s catch-all `exit(0)`
82
+ is the wrong default for a security boundary; and `hooks.json` should ship
83
+ with at least the safety guard enabled plus a setup step that proves the
84
+ harness honors it (harnesses ignore unknown/unenabled hooks silently).
85
+ 3. **Make LANGUAGE generative or say it isn't.** Either branch
86
+ `bootstrap_workspace.sh` per language (build manifests + pinned linter
87
+ configs per `clean_code.md` table) or change the skill wording to record the
88
+ language decision for the agent to implement in Phase 4.
89
+ 4. **Close the benchmark gap this reveals.** `benchmark/RESULTS.md` Arm B/C
90
+ assume working hooks + boundary engine; in a real scaffolded project neither
91
+ is present. A fourth arm — *raw scaffolded project, no agent-authored
92
+ additions* — would measure the actual out-of-box enforcement: expect 0/3
93
+ traps caught.
94
+
95
+ ---
96
+
97
+ ## Addendum (2026-10-08): findings fixed — verify, don't trust
98
+
99
+ The three findings above are closed by the enforcement change set; each fix
100
+ names the test that locks it:
101
+
102
+ 1. **No deterministic linter → per-language gate configs emitted.**
103
+ `bootstrap_workspace.sh` now writes the pinned config for the recorded
104
+ language (`eslint.config.js`, `ruff.toml`, `clippy.toml`, `.golangci.yml`,
105
+ `checkstyle.xml`, `.editorconfig` CA block, `.clang-tidy`; `generic` emits
106
+ none). Locked by `tests/bootstrap-toolchain.test.ts` (9 tests); presence is
107
+ re-checked on every `validate` run by the new phase 7
108
+ (`scripts/validate/toolchain.js`, 100% covered).
109
+ 2. **LANGUAGE decorative → generative for toolchain.**
110
+ `backend+typescript` and `backend+python` still share directory trees
111
+ (topology-scoping, by design) but now differ in emitted gate configs; the
112
+ profile feed (`readProfileLanguage`) drives the validator. The agent still
113
+ owns build manifests (naming decisions) plus tool install and the Phase 5
114
+ live-block proof (`lets-build/SKILL.md`).
115
+ 3. **Fail-open guard → vendored engine + fail-closed install.**
116
+ The guard engine ships inside `.agents/` (`.agents/lib/`, byte-locked to
117
+ `lib/` output by test), `agent_guard.js` resolves vendored-first, and a
118
+ missing engine exits 2 naming every path tried. Unparseable *payloads*
119
+ still fail open inside `inspectPreTool` per ADR-005 (ambiguous input, not a
120
+ broken install). `hooks.json` (+`.example`) wires an `architectural-guard`
121
+ PreToolUse entry. Locked by the three new `tests/agent-guard.test.ts` cases.
122
+ 4. **Benchmark assumes working hooks → Arm D measures the scaffold.**
123
+ `runArmD()` + `auditScaffoldEnforcement()` score a real scaffold before and
124
+ after bootstrap; `benchmark/RESULTS.md` §5 carries the scorecard.
125
+ 5. **Echo lint placeholder → rewired for Node profiles.** `bootstrap` points
126
+ the starter `lint` script at the emitted `eslint.config.js` (placeholder
127
+ only, never agent-wired entries; skipped when node is unavailable), so
128
+ `npm run lint` fails until the pinned tool lands instead of echoing
129
+ success.
130
+
131
+ ## Addendum (2026-10-09): boundaries shipped, starter CI generated, hooks on
132
+
133
+ 6. **Boundary engine now ships in the scaffold.** `lib/boundaries.js` is
134
+ vendored into `.agents/lib/boundaries.js` (byte-locked by
135
+ `tests/boundary-guard.test.ts`, re-copied after every build per
136
+ `CONTRIBUTING.md`) and a vendored-first runner `.agents/scripts/boundary_guard.js`
137
+ exits 0/1 on a repo's cycle & layer verdict (exit 2 on a missing engine).
138
+ The starter `package.json` gains a `boundaries` script pointing at it.
139
+ 7. **Generated CI is now green out of the box.** azcodr's own dev workflows
140
+ (`ci.yml`, `publish.yml`) call scripts a fresh project does not have, so
141
+ shipping them verbatim made a scaffolded repo's CI fail on every push. The
142
+ scaffolder now replaces them with a starter `.github/workflows/ci.yml` that
143
+ only needs Node, the vendored boundary guard, and `scripts/validate-cli.js`.
144
+ Locked by `tests/scaffold-ci.test.ts`.
145
+ 8. **Hooks ship enabled.** `.agents/hooks.json` (+`.example`) now ships all four
146
+ hooks `enabled: true` (safety, architectural, post-tool-lint, stop-verifier),
147
+ amending ADR-020 which deliberately shipped them disabled. The trade-off is
148
+ recorded in the 2026-10-09 ADR: default-on means a harness that loads the
149
+ file blocks immediately, at the cost that lint/stop hooks can interrupt
150
+ worthless-but-harmless sessions in a bare scaffold.
151
+
152
+ ## Reproduce
153
+
154
+ ```bash
155
+ # 1. Scaffold inventory
156
+ node -e "const {scaffold}=require('./lib/scaffold.js'); scaffold({targetDir:'<tmp>',force:true,noGit:true})"
157
+ # 2. Topology x language matrix (Git Bash)
158
+ bash .agents/skills/lets-build/scripts/bootstrap_workspace.sh <tmp> backend python
159
+ # 3. Guard fail-open proof
160
+ echo '{"tool_name":"run_command","tool_input":{"command":"rm -rf /"}}' \
161
+ | node <scaffold>/.agents/scripts/agent_guard.js; echo "exit=$?"
162
+ ```
@@ -57,13 +57,13 @@ Before writing production code for any new feature or user story, the agent must
57
57
  Prevent AI-generated code rot using automated fitness functions integrated into linting and continuous verification:
58
58
  - **File Length Gates**: Maximum 250–300 lines per file (ESLint `max-lines`).
59
59
  - **Function Length Gates**: Maximum 20–30 lines per function (ESLint `max-lines-per-function`).
60
- - **Dependency Direction Gates**: Enforce unidirectional import rules (e.g. `import/no-restricted-paths`, `dependency-cruiser`, `ArchUnit`) ensuring domain core never imports infrastructure or transport adapters.
60
+ - **Dependency Direction & Boundary Gates**: Enforce unidirectional import rules and cycle prevention natively with Azcodr's zero-dependency boundary linter (`npx azcodr boundaries [dir]`) or polyglot equivalents (ArchUnit, dependency-cruiser) ensuring domain core never imports infrastructure or transport adapters, and circular dependencies are forbidden.
61
61
  - **Complexity Budgets**: Enforce cyclomatic complexity limits (maximum 10 per function).
62
62
  If an AI attempt to add code violates any fitness function, the build fails immediately, blocking completion until the architecture is refactored.
63
63
 
64
64
  ### Polyglot Fitness Function Standards
65
65
 
66
- When bootstrapping non-TypeScript projects via `/lets-build`, the agent must instantiate deterministic, automated fitness function gates matching these exact rules:
66
+ When bootstrapping projects via `/lets-build`, `bootstrap_workspace.sh` emits the pinned gate configuration for the recorded language (never invented, never absent); the agent then installs the named tool, wires the project's lint entry to it, and proves the gate in Phase 5. Gates must match these exact rules:
67
67
 
68
68
  | Language | Linter / Static Tool | Max File Lines (300) | Max Function Lines (30) | Max Complexity (10) | Max Arguments (3) |
69
69
  |---|---|---|---|---|---|
@@ -73,3 +73,4 @@ When bootstrapping non-TypeScript projects via `/lets-build`, the agent must ins
73
73
  | **Go** | `golangci-lint` | `maintidx: 300` | `funlen: lines = 30` | `gocyclo: min-complexity = 10` | `funlen: statements = 25` |
74
74
  | **Java** | `Checkstyle` + `ArchUnit` | `FileLength: max 300` | `MethodLength: max 30` | `CyclomaticComplexity: max 10` | `ParameterNumber: max 3` |
75
75
  | **C# / .NET** | `.editorconfig` + Roslyn | `file_length = 300:error` | `method_length = 30:error` | `CA1502: Avoid excessive complexity <= 10` | `CA1501: max 3 params` |
76
+ | **C / C++** | `clang-tidy` | project lint entry (no native check) | `readability-function-size LineThreshold = 30` | `readability-function-cognitive-complexity Threshold = 10` | `readability-function-size ParameterThreshold = 3` |