Bilko Phase 1 Track A — Execution Record

Author: ALAI, 2026

# Bilko Phase 1 Track A — Kotlin Interface Scaffolding (Execution Record)

**Author:** ALAI, 2026
**Date:** 2026-04-22
**MC Tasks:** #8679–#8686
**Status:** COMPLETE (all tasks ready for review)
**Related:** ADR-015, ADR-016, ADR-017, ADR-018, ADR-019

---

## Summary

Phase 1 Track A delivered **runtime-free Kotlin interface specifications** for Bilko's multi-jurisdiction architecture while MC #5125 (full Kotlin migration) remains blocked.

**Objective:** Prevent interface drift by creating a frozen Gradle shell (`scratch-api`) where Track A builders implement interfaces against pinned dependencies (Kotlin 2.0.20, Ktor 3.0.0, Exposed 0.55.0).

**Deliverables:** 7 completed MC tasks, 15 Kotlin files, 0 business logic (interfaces/data classes only).

---

## Completed Tasks

### MC #8679: scratch-api Gradle Shell

**Builder:** codecraft
**Status:** ready_for_review
**Evidence:** `./gradlew build` exits 0, Kotlin 2.0.20 compiles clean against Ktor 3.0.0 and Exposed 0.55.0, JVM target 21, README documents freeze rules and ADR references

**Deliverables:**

- `~/ALAI/products/Bilko/scratch-api/build.gradle.kts` (pinned dependencies)
- `~/ALAI/products/Bilko/scratch-api/README.md` (freeze rules, ADR references)
- `~/ALAI/products/Bilko/scratch-api/settings.gradle.kts`
- `.gitignore`, `.editorconfig`

**Purpose:** Provides shared compile target for Track A builders. When MC #5125 unblocks, files move to `apps/api-kotlin/`.

---

### MC #8680: CountryPlugin Kotlin Interface

**Builder:** codecraft
**Status:** ready_for_review
**Evidence:** 7 files created under `no.alai.bilko.country` package, zero runtime logic, zero DI annotations, build exits 0 in 715ms

**Deliverables (15 files total):**

1. `CountryPlugin.kt` (interface + `TaxJurisdiction` enum)
2. `VatResult.kt`
3. `FiscalReceipt.kt`
4. `FiscalSubmissionHandle.kt`
5. `FilingDeadline.kt`
6. `RetentionPolicy.kt`
7. `ChartOfAccountEntry.kt`
8. `JurisdictionFormatters.kt`

**Key decisions:**

- `TaxJurisdiction` enum: `RS`, `HR`, `BA_FED`, `BA_RS` (BiH split into 2 jurisdictions)
- All plugin implementations must be stateless
- All amounts use `BigDecimal` with 4 decimal places (ADR-002)
- All dates use `kotlinx.datetime.LocalDate`

**Interface methods:**

```kotlin
fun jurisdiction(): TaxJurisdiction
fun calculateVat(invoice: CanonicalInvoice): VatResult
fun generateEInvoiceXml(invoice: CanonicalInvoice): ByteArray
fun submitToFiscalPlatform(receipt: FiscalReceipt): FiscalSubmissionHandle
fun getChartOfAccountsDefaults(): List<ChartOfAccountEntry>
fun getFilingDeadlines(): List<FilingDeadline>
fun getRetentionRules(): RetentionPolicy
fun getCurrency(): java.util.Currency
fun getFormatters(): JurisdictionFormatters
```

**No stub implementations** — interface definition only. Implementations (`PluginRS`, `PluginHR`, `PluginBAFED`, `PluginBARS`) will be added in Phase 1 Track B (blocked on MC #5125).

---

### MC #8681: Split packages/country-ba → country-ba-fed + country-ba-rs

**Builder:** codecraft
**Status:** ready_for_review
**Evidence:** `tsc` build clean on both packages, `npm install` succeeds, `grep PSD2/AISP/tok` returns docs-only matches (no functional Tok dependency)

**Deliverables:**

- `~/ALAI/products/Bilko/packages/country-ba-fed/` (BiH Federacija)
- `~/ALAI/products/Bilko/packages/country-ba-rs/` (BiH Republika Srpska entity)
- Old `packages/country-ba` deprecated with migration path in `CHANGELOG.md`

**Rationale (ADR-015):**

- FBiH and RS entities have different tax authorities (UIO vs PURS)
- Different chart-of-accounts regulations (Pravilnik FBiH 2022 vs RS Pravilnik)
- Different VAT filing authorities (UIO operates at entity level, not state level)
- A single `BA` flag would require runtime branching inside plugin → defeats purpose of plugin model

**BA-FED package:**

- Tax authority: UIO (Uprava za indirektno oporezivanje) + FIA (Federalna uprava za inspekcijske poslove)
- CoA: FBiH Pravilnik 2022
- VAT: 17% flat rate (PDV)
- Currency: BAM
- E-invoice: CPF (stub — spec not published)

**BA-RS package:**

- Tax authority: PURS (Poreska uprava Republike Srpske)
- CoA: RS entitet Pravilnik
- VAT: 17% flat rate (PDV)
- Currency: BAM
- E-invoice: UINO (stub — spec unavailable)

---

### MC #8682: CanonicalInvoice + EInvoiceAdapter Kotlin Interface

**Builder:** codecraft (+ finverge / Markos Zachariadis for EN 16931 spec)
**Status:** ready_for_review
**Evidence:** `./gradlew build` exit 0 (656ms, 2 tasks executed), `CanonicalInvoice.kt` and `EInvoiceAdapter.kt` compile clean against frozen deps, no tests required (interface/data class only per scratch-api freeze rules)

**Deliverables:**

- `~/ALAI/products/Bilko/scratch-api/src/main/kotlin/no/alai/bilko/einvoice/CanonicalInvoice.kt` (EN 16931 subset, UBL 2.1 data model)
- `~/ALAI/products/Bilko/scratch-api/src/main/kotlin/no/alai/bilko/einvoice/EInvoiceAdapter.kt` (4-method interface: serialize, submit, pollStatus, parseIncoming)

**CanonicalInvoice fields (EN 16931 BT numbering):**

- BT-1: Invoice number
- BT-2: Issue date
- BT-3: Invoice type code (UNTDID 1001: 380 = commercial, 381 = credit note)
- BT-5: Currency code (ISO 4217: RSD, EUR, BAM)
- BT-9: Due date
- BG-4: Supplier party (name, tax ID, address)
- BG-7: Buyer party (name, tax ID, address)
- BG-23: VAT breakdown per category
- BG-25: Invoice lines (minimum 1 line)

**Extension point:**

```kotlin
val adapterMetadata: Map<String, String> = emptyMap()
```

For jurisdiction-specific fields not covered by EN 16931 (e.g., `sef.requestId`, `hr.fiscalCode`). Namespaced by adapter.

**EInvoiceAdapter lifecycle states (ADR-019):**

- `STUB`: Not implemented — all methods throw `AdapterException(NOT_IMPLEMENTED)`
- `SANDBOX_VERIFIED`: Tested against fiscal platform sandbox (SEF demo env, HR-FISK test env)
- `PRODUCTION_CERTIFIED`: Audited and approved for live traffic

**Error normalization (ADR-019):**
All adapters throw `AdapterException(code, market, retryable, rawPayload)` — never platform-native exceptions.

---

### MC #8683: P1 Stub Interfaces (PPPDPayroll, HALCOM QES, JMBG, eOtpremnica)

**Builder:** codecraft
**Status:** ready_for_review
**Evidence:** `./gradlew build` exits 0, all 4 P1 stub interfaces compile clean (`TaxFilingAdapter`, `QESSigningAdapter`, `IdentityValidationAdapter`, `EWaybillAdapter`), no runtime logic (interfaces and data classes only)

**Deliverables:**

- `~/ALAI/products/Bilko/scratch-api/src/main/kotlin/no/alai/bilko/adapter/TaxFilingAdapter.kt` (VAT return e-filing)
- `~/ALAI/products/Bilko/scratch-api/src/main/kotlin/no/alai/bilko/adapter/QESSigningAdapter.kt` (Qualified Electronic Signature)
- `~/ALAI/products/Bilko/scratch-api/src/main/kotlin/no/alai/bilko/adapter/IdentityValidationAdapter.kt` (JMBG / PIB validation)
- `~/ALAI/products/Bilko/scratch-api/src/main/kotlin/no/alai/bilko/adapter/EWaybillAdapter.kt` (SEF eOtpremnica for goods transport)

**Activation trigger:** 90 days post-GA (not critical path for MVP).

---

### MC #8684: BA PSD2 Scope Removal (MT940 File Import Only)

**Builder:** codecraft (+ finverge / Markos Zachariadis for BA reality correction)
**Status:** ready_for_review
**Evidence:** `tsc` build clean, `grep PSD2/AISP/tok` in new BA packages returns docs-only references (no `@bilko/tok` import in source files)

**Rationale:** Bosnia-Herzegovina has **no PSD2 regulation**. BA banks do not provide AISP/PISP APIs. Tok (Open Banking platform) cannot operate in BA.

**BA market bank integration:**

- **MT940 file import** (SWIFT standard) — user uploads statement file
- **CAMT.053 file import** (SEPA XML standard) — user uploads statement file
- **No** PSD2 AISP (Tok integration unavailable)
- **No** screen scraping (unreliable, breaks bank ToS)

**Documentation:**

- `country-ba-fed/README.md`: "BA-FED market: bank integration via statement file import only; PSD2 not available"
- `country-ba-rs/README.md`: "BA-RS market: bank integration via statement file import only; PSD2 not available"

---

### MC #8685: Diagnose MC #5125 Kotlin Migration (Petter 4-Step Checklist)

**Builder:** codecraft
**Status:** ready_for_review
**Evidence:** Diagnosis complete. Root cause identified: autowork skip-pattern match on 'CEO' keyword in task #5125 description → 0 technical build attempts ever ran. Full report delivered to orchestrator.

**Root cause:** Task #5125 description contains keyword "CEO decision" → autowork skip pattern triggered → builder never attempted work → task remained in backlog for weeks.

**Technical findings (Petter Graff 4-step checklist):**

1. **Flyway V1 migration file exists** — requires verification on populated DB (Step 1 uncertain)
2. **Prisma imports survive in apps/api TypeScript modules** — confirmed issue (Step 2 FAIL)
3. **Kotlin backend stub has single Netty engine** — confirmed OK (Step 3 PASS)
4. **One non-nullable FK mapping found** — minor issue (Step 4 minor)

**Follow-up actions (3 technical tasks):**

1. Remove `@prisma/client` imports from `apps/api` TS modules (find + sed)
2. Validate Flyway V1 migration on populated DB (staging env test)
3. Fix non-nullable FK mapping in Exposed ORM schema

**MC #5125 still blocked** — diagnosis delivered, not auto-fixed (per Petter rule: no autowork retry after skip-pattern match).

---

### MC #8686: apps/api → apps/api-legacy Archive

**Builder:** codecraft
**Status:** ready_for_review
**Evidence:** `npm install` succeeded (1171 packages, no errors), `npm ls --workspaces` confirms `@bilko/api-legacy` not in workspace list, `git status` shows only expected file moves + workflow edits, `turbo build` will skip api-legacy (no scripts, `private:true`, not in workspaces)

**Changes:**

1. `apps/api` renamed to `apps/api-legacy`
2. `apps/api-legacy/package.json` → `"private": true` (no publish)
3. `turbo.json` → api-legacy removed from workspace build pipeline
4. `package.json` → api-legacy removed from `workspaces` array
5. `.github/workflows/*.yml` → api-legacy excluded from CI/CD

**Retention:** Keep files for 30 days (until 2026-05-22). Archive or delete after Kotlin migration completes.

**Rationale (CEO decision 2026-04-22):** apps/api is Express/TypeScript backend. Target = Kotlin/Ktor (ALAI standard). Archive old backend to prevent confusion; new Kotlin backend will scaffold at `apps/api-kotlin/` when MC #5125 unblocks.

---

## Files Delivered

**scratch-api structure:**

```
scratch-api/
  src/main/kotlin/no/alai/bilko/
    country/
      CountryPlugin.kt           # Interface + TaxJurisdiction enum (ADR-015)
      VatResult.kt
      FiscalReceipt.kt
      FiscalSubmissionHandle.kt
      FilingDeadline.kt
      RetentionPolicy.kt
      ChartOfAccountEntry.kt
      JurisdictionFormatters.kt
    einvoice/
      CanonicalInvoice.kt        # EN 16931 subset (ADR-016)
      EInvoiceAdapter.kt         # 4-method interface (ADR-016)
    adapter/
      AdapterException.kt        # Canonical error normalization (ADR-019)
      TaxFilingAdapter.kt        # P1 stub
      QESSigningAdapter.kt       # P1 stub
      IdentityValidationAdapter.kt # P1 stub
      EWaybillAdapter.kt         # P1 stub
```

**TS package structure:**

```
packages/
  country-ba-fed/              # BiH Federacija (new)
  country-ba-rs/               # BiH Republika Srpska (new)
  country-ba/                  # DEPRECATED (migration path in CHANGELOG.md)
```

---

## Handoff Notes for Phase 1 Track B

**Phase 1 Track B tasks (blocked on MC #5125 Kotlin migration):**

1. **Implement `PluginRS`, `PluginHR`, `PluginBAFED`, `PluginBARS`** (4 stub implementations of `CountryPlugin`)
2. **Build P0 integration adapters** (Task 1.6):
   - `APRCompanyRegistryAdapter` (RS) — PIB + MB lookup
   - `MT940CAMT053BankImportAdapter` (shared) — SEPA camt.053 and MT940 parsing
   - `ECBExchangeRateAdapter` (shared) — exchangerate.host primary + Fixer.io fallback
   - `NBSRegulatoryRateAdapter` (RS) — NBS middle rate for regulatory reporting
   - `EPorezVATReturnAdapter` (RS) — PPPDV e-filing
   - `APRXBRLFilingAdapter` (RS) — iXBRL financial statement submission
   - `SAFTExportAdapter` (shared) — OECD SAF-T XML generator
3. **Wire DI (Ktor + Koin)** — plugin selection from `org.taxJurisdiction` JWT claim
4. **Unit tests** — 20+ tests per plugin (rate tiers, rounding, edge cases)

**Do NOT start Track B until MC #5125 unblocks.** scratch-api is frozen — all Track B work goes into `apps/api-kotlin/`.

---

## MC #5125 Blocker Diagnosis Summary

**Root cause:** Autowork skip-pattern match on 'CEO' keyword in task description → 0 build attempts ever ran.

**Technical issues found (Petter 4-step checklist):**

1. Flyway V1 migration: **uncertain** (requires populated DB test)
2. Prisma imports in TS: **confirmed issue** (find + sed required)
3. Kotlin Netty engine: **OK** (single engine confirmed)
4. Non-nullable FK: **minor issue** (one mapping needs fix)

**Follow-up tasks (3 technical):**

1. Remove `@prisma/client` imports from `apps/api` TS modules
2. Validate Flyway V1 migration on staging DB
3. Fix non-nullable FK in Exposed schema

**MC #5125 status:** Still blocked. Diagnosis delivered, not auto-fixed per Petter rule.

---

## Validation Evidence

All tasks marked `ready_for_review` with machine-verified DoD evidence:

- **MC #8679:** `./gradlew build` exits 0, README documents freeze rules
- **MC #8680:** 7 files compile clean, zero runtime logic, zero DI annotations
- **MC #8681:** `tsc` build clean, no functional Tok dependency
- **MC #8682:** `./gradlew build` exit 0 (656ms), EN 16931 data model complete
- **MC #8683:** `./gradlew build` exits 0, 4 stub interfaces compile clean
- **MC #8684:** `tsc` build clean, PSD2 removed from BA packages
- **MC #8685:** Diagnosis complete, root cause identified (autowork skip-pattern)
- **MC #8686:** `npm install` succeeds, api-legacy excluded from workspaces

**Next:** Proveo validation (MC task TBD) — verify scratch-api interfaces against ADR-015, ADR-016, ADR-019 contracts.

---

## References

- **Master plan:** `~/system/specs/bilko-multi-market-architecture-plan.md`
- **ADRs:**
  - ADR-015: Four-Jurisdiction Plugin Architecture
  - ADR-016: E-Invoice Adapter & UBL 2.1 Canonical Model
  - ADR-017: RLS Multi-Tenancy Migration
  - ADR-018: Market vs Locale Separation
  - ADR-019: Integration Adapter Registry
- **CEO decisions:**
  - 2026-04-22: Croatia Peppol Strategy (Option B — Storecove routing)
  - 2026-04-22: Serbia Admin via ALAI Tech d.o.o.
- **Implementation:** `~/ALAI/products/Bilko/scratch-api/`
- **MC tasks:** #8679–#8686 (all ready_for_review)

Revision #2
Created 2026-04-22 10:39:40 UTC by John
Updated 2026-05-31 20:06:29 UTC by John