Files
boshu2__agentops/docs/learnings/2026-05-13-bc-ports-wire-up-arc.md
T
Boden Fuller 4226b9b395 docs(learnings): capture BC ports wire-up arc (cycle 122)
26-cycle structural milestone deserves a durable learning so future
/evolve cycles can find it via `ao lookup` / docs/learnings/
browsing and not re-discover the same shape patterns.

New file: docs/learnings/2026-05-13-bc-ports-wire-up-arc.md.
(First attempt wrote to .agents/learnings/ which is gitignored —
moved to the tracked docs/learnings/ home that sibling learnings
2026-05-11-* and 2026-05-12-* live in.)

Captures:

1. What the arc proved — repeatable file triplet (~250 LOC + ~10
   tests per cycle), 8 distinct adapter shapes that all worked
   under the same triplet, reader+writer pair tests prove on-disk
   format compatibility, two valid subprocess-test shapes (fake-
   script vs pluggable func field).

2. Anti-patterns observed — fileExists collision caught by go vet
   (cycle 115), cap shadowing (cycle 117), assuming wrong adapter
   shape until re-reading port contract (cycle 116). All three
   have actionable preventions ("grep before util", "rename when
   shadowing builtins", "read port doc-comment first").

3. What the arc did NOT do — no call-site migration, no cross-
   process EventBus. Names the per-BC follow-up bds tracking that
   work and the operator transport-choice still deferred.

4. WHY the arc stopped at 12 adapters instead of migrating callers
   — intra-package routing (cli/cmd/ao → cli/cmd/ao) adds layers
   without semantic gain because the port surface only matters at
   a package boundary. The genuine value comes from cross-package
   callers or test substitution. Documents the next-phase target
   explicitly so cycle 123+ doesn't pick a manufactured migration.

5. Cycle-history pointers — scaffold arc 78-106, wire-up arc
   83+108-118, doc reconciliation 119-121, total 26 cycles across
   the BC ports surface.

Why this is a cycle-worth-doing: the cycle 122 selection ladder had
no fresh signal post-wire-up. Capturing the structural milestone
preserves the lessons for future operators and gives docs/learnings/
something concrete to surface when "ports", "wire-up", or
"hexagonal" comes up in a future session.

Cycle 122 / soc-2klg knowledge-capture mode.
2026-05-12 20:26:52 -04:00

4.4 KiB

title, date, tags, source, maturity
title date tags source maturity
BC ports wire-up arc — 12 production adapters in 11 consecutive cycles 2026-05-13
hexagonal-architecture
ddd-bounded-context
ports
wire-up
evolve-loop
.agents/evolve/cycle-history.jsonl cycles 83+108-118 lessons-learned

BC ports wire-up arc

11 consecutive /evolve cycles (108-118), preceded by cycle 83's first-mover productionCitationAdapter, delivered 12 production adapters covering all 5 DDD bounded contexts (BC1-BC5). The arc is the canonical compound-by-repetition demonstration in the cycle history. Cycles 119-121 reconciled doc-narrative drift after the arc completed.

What the arc proved

  • A repeatable file shape ships fast. Every adapter followed the same triplet: <port>_adapter.go in cli/cmd/ao/, paired <port>_adapter_test.go, compile-time var _ XPort = (*productionX)(nil) assertion. Average cycle: ~250 LOC + ~10 tests in ~10 minutes wall-clock.
  • 8 distinct adapter shapes all worked under the same triplet: CLI wrapper (Citation), JSONL append/read (Loop R/W, Operator, ClaimEvidenceBinder w/ upgrade-only rule), tree-walk + hash join (Harness), tree-walk + ranker (CorpusReader), idempotent file write + frontmatter (CorpusWriter), pure-Go transform (FindingCompiler), subprocess bash wrapper (GateRunner), external CLI + pluggable runner (CIStatus), sync in-memory pubsub (EventBus).
  • Reader+writer pair tests work. Both BC1 (CorpusReader+Writer cycles 112-113) and BC3 (LoopReader+Writer cycles 108-109) shipped round-trip tests in the writer's suite — proves the on-disk format is shared end-to-end.
  • Subprocess testing has two valid shapes. Cycle 115's GateRunner used a fake-bash-script-in-temp-repo harness; cycle 117's CIStatus refined to a pluggable runGH func field that lets tests substitute canned bytes without writing a script. Both are valid; the func-field shape is faster and platform- independent (use it when the adapter's interesting logic is the parser, not the subprocess invocation).

Anti-patterns observed during the arc

  • Naming collisions caught by go vet. Cycle 115 introduced a local fileExists helper that collided with doctor.go's existing one. go vet caught it; fix was to reuse doctor's helper rather than rename. Lesson: before writing a small util in an adapter, grep -n "func <name>" cli/cmd/ao/ first.
  • Shadowing builtins. Cycle 117 wrote cap := limit which shadowed cap(). go vet didn't fail (it's legal) but it's a readability trap. Renamed to pageSize.
  • Assuming the wrong adapter shape. Cycle 116 (ClaimEvidenceBinder) initially looked like a bd-CLI wrapper (cycle 83 Citation precedent), but re-reading the port contract showed it's a JSONL adapter (the binding ledger lives on disk, not in bd). Lesson: always read the port doc-comment first; the adapter shape is constrained by the port's persistence model.

What the arc did NOT do

  • No call-site migration. Production callers in cli/cmd/ao still invoke the leaf helpers (e.g. beads.go:227 still calls verifyCitationInPlace directly, not via the productionCitationAdapter). The adapters are reachable but latent. The per-BC follow-up bds (soc-pm5t for BC1.1, etc.) track the migration work.
  • No cross-process EventBus. productionEventBus is sync in-process; the adapter doc explicitly names the swap path (event_bus_nats.go or similar) for when the factory goes distributed. Operator transport-choice is deferred.

Why I stopped at 12 instead of migrating callers

A migration cycle inside cli/cmd/ao adds a layer (verifyCitationInPlace → productionCitationAdapter → verifyFileCitation) without semantic gain because both endpoints are in the same package — the port surface only matters at a package boundary. The genuine call-site value comes when:

  • a non-cli/cmd/ao package consumes one of these adapters, or
  • a test substitutes the InMemoryX adapter to exercise a code path without real I/O.

Future cycle should pursue the former (create or migrate a cross-package caller) rather than manufacture intra-package routing.

Cycle-history pointers

  • Scaffold arc: cycles 78-106 (12 port interfaces + InMemoryX adapters, captured in docs/contracts/bc-ports-inventory.md)
  • Wire-up arc: cycle 83 + cycles 108-118 (12 production adapters)
  • Doc reconciliation arc: cycles 119-121 (3 doc files updated to reflect actual state)
  • Total: 26 cycles across the BC ports surface