Skip to content

STORY-F-002: Boundary library config + CI enforcement (AR-07)

Epic: Foundation Priority: Must Have Story Points: 2 Status: Not Started Assigned To: Unassigned Created: 2026-04-17 Sprint: 1


User Story

As the Lead Developer, I want Boundary library enforcing cross-domain communication rules at compile time with CI blocking violations, so that the AR-07 "events-only cross-domain" rule is enforced by tooling (per Commandment #31) and cannot erode under pressure.


Description

Background

ADR-005-F mandates events-only cross-domain communication. Commandment #31 says boundaries enforced by convention alone will erode — tooling must enforce. The Boundary library (saleyn/boundary hex package by Saša Jurić) inspects compile output and fails when a module references another boundary it hasn't declared a dependency on.

This is the only acceptable structural defence against a future developer adding alias Finnest.Payroll.Something inside Finnest.Roster — which would silently break ADR-002-F / ADR-005-F.

Scope

In scope:

  • Add {:boundary, "~> 0.10", runtime: false} to umbrella root mix.exs
  • .boundary.exs at umbrella root declaring allowed dependencies:
  • Every domain app → finnest_core allowed
  • finnest_web → any domain allowed (HTTP edge exception)
  • finnest_agents → any domain MCP allowed (agent-tier exception)
  • finnest_corenothing (pure foundation)
  • Domain A → Domain B forbidden
  • The single sync exception (Finnest.Compliance.check/2) allowed for 5 callers only (roster, timekeep, payroll, clients, assets) — note this is Phase 0 scaffolding; the actual check function lands with compliance domain in F-015 / F-018 / later stories
  • Boundary annotations in each app's lib/finnest_<domain>.ex declaring use Boundary, top_level?: true or equivalent
  • CI integration: mix boundary runs in .github/workflows/ci-finnest.yml (added in F-005) and fails the build on violation
  • Pre-commit hook (local) mix boundary runs as part of mix check

Out of scope:

  • Runtime boundary enforcement (not needed — compile-time is enough)
  • Exceptions for test-only code (handle if friction arises in F-007)

Technical Notes

  • Boundary docs: https://hexdocs.pm/boundary/Boundary.html
  • The tricky part is the Finnest.Compliance.check/2 exception — only 5 apps may call it. Recommended approach: declare a compliance_consumer sub-boundary that exposes only Finnest.Compliance.check/2 and list roster/timekeep/payroll/clients/assets as permitted callers.
  • Write an architecture test in test/architecture/boundary_test.exs that asserts mix boundary exits zero on a clean build — belt-and-braces beyond the CI step
  • Reference Saša Jurić's blog posts on Boundary (search "boundary elixir jurić") for patterns

Dependencies

  • Blocked by: STORY-F-001 (umbrella must exist)

Acceptance Criteria

  • boundary dep added to umbrella mix.exs
  • .boundary.exs at umbrella root declares all app boundaries + dependency rules
  • Each app has use Boundary declaration in its top-level facade module
  • mix boundary completes with zero violations on clean scaffold
  • Deliberate test: add alias Finnest.Payroll inside Finnest.Rostermix boundary fails the build (demonstrate, then revert)
  • Deliberate test: finnest_core trying to alias Finnest.Webmix boundary fails (demonstrate, then revert)
  • Finnest.Compliance.check/2 callable from roster/timekeep/payroll/clients/assets only — confirmed via boundary rules (stub function is fine for now; real impl in later story)
  • mix boundary runs as part of CI (F-005) — asserted in CI config
  • Architecture test file test/architecture/boundary_test.exs asserts clean state

Testing Requirements

  • Architecture test test/architecture/boundary_test.exs runs mix boundary --format json and asserts zero violations
  • Manual validation test (documented in story): introduce a violation, observe mix boundary failure, revert
  • CI integration tested by pushing a deliberate violation to a feature branch and confirming CI rejects merge

References