[AZ-485] Add Public API barrels + STC-ARCH-01 (F4 close)

Closes architecture baseline finding F4. Every component now exposes
its Public API through `src/<component>/index.ts`; cross-component
imports go through the barrel. `scripts/check-arch-imports.mjs` plus
`STC-ARCH-01` in the static profile enforce the rule; tests in
`tests/architecture_imports.test.ts` cover AC-4/AC-5 + 2 exemption
cases. One F3-pending exemption (`classColors`) is documented in 5
places (barrel, consumer, script, doc, test) to avoid a circular
import.

Phase B cycle 1 batch 1 of 2 (epic AZ-447). Batch 2 is AZ-486
(endpoint builders) — blocked on this commit landing.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-11 10:33:30 +03:00
parent 2071a24391
commit 23746ec61d
56 changed files with 455 additions and 101 deletions
+90
View File
@@ -0,0 +1,90 @@
import { afterEach, describe, expect, it } from 'vitest'
import { spawnSync } from 'node:child_process'
import { writeFileSync, rmSync, mkdirSync, existsSync } from 'node:fs'
import { join, resolve } from 'node:path'
// AZ-485 / F4 — verifies the STC-ARCH-01 static gate (scripts/check-arch-imports.mjs):
// - AC-5 : passes on the migrated codebase as-is
// - AC-4 : fails when a synthetic cross-component deep import is added
// - AC-4 : ignores the F3-pending exemption (features/annotations/classColors)
// - AC-4 : ignores deep imports written inside // line comments
//
// Exercises the actual gate via subprocess so a regression in the script
// (regex drift, exemption logic, exit code) trips the test even if the bash
// glue in run-tests.sh keeps reporting PASS.
//
// All offending substrings are built via concatenation at runtime so the
// scanner itself does not flag this test file when it walks `tests/**`.
const REPO_ROOT = resolve(__dirname, '..')
const SCRIPT = join(REPO_ROOT, 'scripts', 'check-arch-imports.mjs')
const FIXTURE_DIR = join(REPO_ROOT, 'tests', '_arch_fixtures')
const FROM = 'fr' + 'om'
const UP2 = '..' + '/..'
const DEEP_API = `${UP2}/src/api/cl` + 'ient'
const DEEP_CLASSCOLORS = `${UP2}/src/features/annotations/classCo` + 'lors'
function runCheck(): { status: number; stderr: string } {
const res = spawnSync('node', [SCRIPT, `--root=${REPO_ROOT}`], {
cwd: REPO_ROOT,
encoding: 'utf8',
})
return { status: res.status ?? -1, stderr: res.stderr ?? '' }
}
function writeFixture(filename: string, content: string): string {
mkdirSync(FIXTURE_DIR, { recursive: true })
const path = join(FIXTURE_DIR, filename)
writeFileSync(path, content, 'utf8')
return path
}
describe('AZ-485 STC-ARCH-01 — no cross-component deep imports', () => {
afterEach(() => {
if (existsSync(FIXTURE_DIR)) rmSync(FIXTURE_DIR, { recursive: true, force: true })
})
it('AC-5: passes on the migrated codebase (no fixtures)', () => {
// Assert
const { status, stderr } = runCheck()
expect(stderr, stderr).toBe('')
expect(status).toBe(0)
})
it('AC-4: FAILS when a deep import into another component is introduced', () => {
// Arrange
const body = `import { api } ${FROM} '${DEEP_API}'\nexport const _force = api\n`
writeFixture('synthetic_deep_import.ts', body)
// Act
const { status, stderr } = runCheck()
// Assert
expect(status).not.toBe(0)
expect(stderr).toMatch(/STC-ARCH-01/)
expect(stderr).toMatch(/synthetic_deep_import\.ts/)
expect(stderr).toMatch(/src\/api\/client/)
})
it('AC-4: still PASSES when only the classColors F3-pending exemption is used', () => {
// Arrange
const body =
`import { FALLBACK_CLASS_NAMES } ${FROM} '${DEEP_CLASSCOLORS}'\n` +
`export const _force = FALLBACK_CLASS_NAMES\n`
writeFixture('classcolors_exemption.ts', body)
// Act
const { status, stderr } = runCheck()
// Assert
expect(stderr, stderr).toBe('')
expect(status).toBe(0)
})
it('AC-4: deep imports inside line comments do not trip the gate', () => {
// Arrange
const body = `// import { api } ${FROM} '${DEEP_API}'\nexport const _x = 1\n`
writeFixture('commented_out_deep_import.ts', body)
// Act
const { status } = runCheck()
// Assert
expect(status).toBe(0)
})
})