Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/scripts/validate-eclexiaiser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env python3

Check failure

Code scanning / Hypatia

Hypatia cicd_rules: banned_language_file Error

Python file detected -- banned language
# SPDX-License-Identifier: MPL-2.0
# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
#
# validate-eclexiaiser.py — structural validation for eclexiaiser.toml
# Extracted from dogfood-gate.yml: an inline `python3 -c` heredoc at column 1
# cannot satisfy both YAML block-scalar indentation and Python's no-indent
# rule for top-level statements, so the workflow failed YAML validation at
# startup (0s, no jobs). Keeping the validator in a real file fixes that and
# makes the logic testable on its own.
"""Validate the structure of ./eclexiaiser.toml (Python 3.11+ tomllib)."""
import tomllib
import sys

with open("eclexiaiser.toml", "rb") as f:
data = tomllib.load(f)

project = data.get("project", {})
if not project.get("name", "").strip():
print("ERROR: project.name is required", file=sys.stderr)
sys.exit(1)

functions = data.get("functions", [])
if not functions:
print("ERROR: at least one [[functions]] entry is required", file=sys.stderr)
sys.exit(1)

for fn in functions:
if not fn.get("name", "").strip():
print("ERROR: function name cannot be empty", file=sys.stderr)
sys.exit(1)
if not fn.get("source", "").strip():
print(f'ERROR: function {fn["name"]} has no source path', file=sys.stderr)
sys.exit(1)

print(f'Valid: {project["name"]} ({len(functions)} function(s))')
26 changes: 4 additions & 22 deletions .github/workflows/dogfood-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -256,28 +256,10 @@ jobs:

echo "has_manifest=true" >> "$GITHUB_OUTPUT"

# Validate TOML structure using Python 3.11+ tomllib
python3 -c "
import tomllib, sys
with open('eclexiaiser.toml', 'rb') as f:
data = tomllib.load(f)
project = data.get('project', {})
if not project.get('name', '').strip():
print('ERROR: project.name is required', file=sys.stderr)
sys.exit(1)
functions = data.get('functions', [])
if not functions:
print('ERROR: at least one [[functions]] entry is required', file=sys.stderr)
sys.exit(1)
for fn in functions:
if not fn.get('name', '').strip():
print('ERROR: function name cannot be empty', file=sys.stderr)
sys.exit(1)
if not fn.get('source', '').strip():
print(f'ERROR: function {fn[\"name\"]} has no source path', file=sys.stderr)
sys.exit(1)
print(f'Valid: {project[\"name\"]} ({len(functions)} function(s))')
" || {
# Validate TOML structure using Python 3.11+ tomllib.
# Script lives in .github/scripts/ so the YAML block scalar and
# Python's indentation rules don't conflict (see that file's header).
python3 .github/scripts/validate-eclexiaiser.py || {
echo "::error file=eclexiaiser.toml::Invalid eclexiaiser.toml — see step output for details"
exit 1
}
Expand Down
1 change: 1 addition & 0 deletions .machine_readable/6a2/STATE.a2ml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ test-coverage = "CLOSED 2026-04-25 — 165 ExUnit tests; CRG C met"

[session-history]
entries = [
{ date = "2026-05-18", description = "k9iser-mcp PR #73 session: (1) Added k9iser-mcp cartridge — reference -iser regeneration-cartridge pattern (central K9 contract regeneration) mirroring ssg-mcp: cartridge.json, mod.js, Idris2 ABI, Zig FFI, panels. (2) Unified transaction-gated adapter: ONE internal/loopback listener, protocol-routed REST+SSE+GraphQL+gRPC-compat → SINGLE dispatch → one Zig ABI, replacing the ssg-era 3-parallel-port anti-pattern; trust gate runs before every dispatch mirroring the Idris2 exposureSatisfied contract (no gatekeeperless path); internal-only behind http-capability-gateway per ADR-0004. (3) boj-rest SSE: POST /cartridge/:name/sse on the same single Cowboy listener + trust-gated dispatch, text/event-stream. (4) Doc reconciliation: elixir/README.adoc, mcp-bridge/api-clients.js, OPERATOR-QUICKSTART.md corrected to the verified runtime + ADR-0004 tiered model (previously wrongly 'skeleton/501/pending rewrite'). (5) CI ROOT-CAUSE FIX: dogfood-gate.yml failed YAML validation at startup (0s, no jobs) on every branch incl. main — inline `python3 -c \"` placed Python at column 1 inside a `run: |` block scalar, terminating the scalar early; since 'Dogfood Gate' is a required status check this silently blocked EVERY PR in the repo. Validator extracted to .github/scripts/validate-eclexiaiser.py and invoked from the workflow (commit 891b162). Verification: Elixir 177/177 (incl. 2 SSE tests); Zig ffi 16/16 + unified adapter 5/5 (exposure-gate truth table mirroring Idris2 contract); idris2 --check K9iserMcp/SafeK9iser.idr passes. Out of scope / separately tracked: http-capability-gateway production-wiring (ADR-0004 tier-2, ~8-12wk) and iseriser-scaffold rollout. Refs hyperpolymath/k9iser#8. NOTE: merge held — GitHub Actions runner-starved estate-wide (standards#122), required checks cannot execute until that clears." },
{ date = "2026-04-27", description = "Worker briefing + hook session: (1) Created .boj/project-memories.a2ml — standing worker briefing template covering coord registration, completion contract ('push succeeds, not TODO list'), backend wiring checklist, banned patterns (unimplemented!/sorry/Admitted/believe_me/.unwrap()-as-debt), comms protocol, git hygiene, and echidna language policy. Committed 039908a + pushed to main. (2) Updated ~/.config/coord-tui/coord-hooks.sh claude() function to auto-prepend project-memories.a2ml via --append-system-prompt when launched from a repo with .boj/project-memories.a2ml. Repos without the file are unaffected." },
{ date = "2026-04-25", description = "Close-out documentation sweep: ROADMAP.adoc current status → 'v1.1.0 Active (Grade C)'; seed node v0.3.0 bullet updated with fly.dev hostnames + LIVE date; Phase 8/9/Grade-C bullets added. EXPLAINME.adoc testing section expanded to 11 modules (173 total, 0 failures); 'harnesses pending' note removed; seed node infrastructure table added. AI-WORK-todo.md §8 header updated from 'in progress' to 'Grade C / 4 nodes LIVE'; docs-sweep bullet added. All changes committed + pushed to main." },
{ date = "2026-04-25", description = "Documentation + deploy sweep session: (1) .dockerignore fixed — ffi/zig/.zig-cache/ (347 MB) + ffi/zig/zig-out/ + cartridges/*/ffi/.zig-cache/ added; reduces build context from ~2.8 GB to ~2.2 MB for fly seed deploys. (2) Containerfile.fly.dockerignore created at repo root (Docker BuildKit per-Dockerfile override) — also excludes cartridges/*/ffi/ + abi/ + adapter/ + panels/ (cartridge source not needed in seed builds). (3) 6a2 sweep: AGENTIC (fly.io pitfall constraints + v0.2.0); ECOSYSTEM (seed-nodes updated with fly.io apps/hosts/regions + dns-status + federation protocol corrected + V-lang ref fixed + v0.2.0); NEUROSYM (report-format logtalk→json + v0.2.0); PLAYBOOK (full operational runbook: local dev, full container, fly.io per-node + deploy-seeds.sh, notes on glibc/musl ABI, .dockerignore, custom domain procedure + v0.2.0); META (languages elixir+javascript added, ADR-010 through ADR-014, design-rationale seed-node-lean-build + trust-level-design + v0.3.0). (4) All 4 fly.io seed nodes deployed: boj-seed-eu (lhr), boj-seed-de (fra), boj-seed-us (iad), boj-seed-ap (syd) — each 2/2 health checks passing, 61 MB image. gap-5 closed." },
Expand Down
38 changes: 38 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,44 @@

All notable changes to Bundle of Joy Server are documented here.

## [Unreleased]

### Added

- **k9iser-mcp cartridge** — reference implementation of the `-iser`
regeneration-cartridge pattern (central K9 contract regeneration), mirroring
ssg-mcp: `cartridge.json`, `mod.js`, Idris2 ABI, Zig FFI, panels.
- **Unified transaction-gated adapter**: one internal/loopback listener,
protocol-routed REST + SSE + GraphQL + gRPC-compat → single dispatch → one
Zig ABI. Replaces the ssg-era 3-parallel-port anti-pattern; the trust gate
runs before every dispatch, mirroring the Idris2 `exposureSatisfied`
contract (no gatekeeperless path). Internal-only behind
`http-capability-gateway` per ADR-0004.
- **boj-rest SSE surface**: `POST /cartridge/:name/sse` on the same single
Cowboy listener and trust-gated dispatch, `text/event-stream`.

### Changed

- **Doc reconciliation to ADR-0004**: `elixir/README.adoc`,
`mcp-bridge/api-clients.js`, and `OPERATOR-QUICKSTART.md` corrected to the
verified runtime + ADR-0004 tiered model (they previously and wrongly
described it as "skeleton/501/pending rewrite").

### Fixed

- **`dogfood-gate.yml` failed YAML validation at startup** (0 s, no jobs) on
every branch including `main`: an inline `python3 -c "` block placed Python
source at column 1 inside a `run: |` block scalar, terminating the scalar
early. Because **Dogfood Gate** is a required status check, this silently
blocked every PR in the repo. The validator now lives in
`.github/scripts/validate-eclexiaiser.py` and is invoked from the workflow.

> Verification (k9iser-mcp): Elixir suite 177/177 (incl. 2 SSE tests); Zig
> ffi 16/16 and unified adapter 5/5 (exposure-gate truth table mirroring the
> Idris2 contract); `idris2 --check K9iserMcp/SafeK9iser.idr` passes.
> http-capability-gateway production-wiring (ADR-0004 tier-2) and the
> iseriser-scaffold rollout remain out of scope and separately tracked.

## [0.4.0] — 2026-04-17

### Changed
Expand Down
65 changes: 65 additions & 0 deletions cartridges/k9iser-mcp/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: PMPL-1.0-or-later
// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
= k9iser-mcp — K9 contract regeneration cartridge
:toc: macro
:orientation: shallow

toc::[]

== Purpose

Wraps the `k9iser` generator as a BoJ cartridge so that a repository's
`generated/k9iser/*.k9` contracts are regenerated *centrally, on trigger*
instead of being run ad hoc and hand-committed (which is how they rot —
see hyperpolymath/k9iser#8 and the idaptik#77 triage).

This is the reference implementation for the `-iser` regeneration-cartridge
pattern (standards epic): every `-iser` generator should ship an equivalent
so `generated/*` stops being hand-maintained drift estate-wide.

== Pipeline

[source]
----
Empty -> ManifestLoaded -> Generated -> Validated -> Applied
----

Enforced safety invariants (formally in `abi/K9iserMcp/SafeK9iser.idr`,
executably in `ffi/k9iser_ffi.zig`):

. *No apply without validate* — contracts that fail the canonical
`k9-validate` ruleset (K9! magic + pedigree) can never be committed back
to a repository.
. *No validate without generate* — stale/absent output is never validated.
. *Generate requires a loaded manifest.*

== Tools (`cartridge.json`)

[cols="1,3"]
|===
| `k9_load_manifest` | Load a repo's `k9iser.toml`
| `k9_generate` | Generate K9 contracts from configs + manifest rules
| `k9_validate` | Validate against the canonical K9! + pedigree ruleset
| `k9_apply` | Commit + push regenerated contracts to the branch
| `k9_clean` | Clean the session
|===

== Layout

* `cartridge.json` — manifest (tools, ADR-0006 ffi symbols)
* `mod.js` — Deno MCP bridge → backend at `K9ISER_BACKEND_URL`
(default `http://127.0.0.1:7743`)
* `abi/` — Idris2 ABI: pipeline state machine + safety GADT
* `ffi/` — Zig FFI: C-ABI exports + ADR-0006 `boj_cartridge_invoke`
dispatch (16/16 tests pass)
* `adapter/` — three-protocol (REST/gRPC-compat/GraphQL) adapter
* `panels/` — Panll dashboard manifest

== Status

Grade D Alpha. Cartridge package + verified state machine are complete.
`boj_cartridge_invoke` returns shaped stubs; the live path additionally
needs the backend service (runs the `k9iser` binary against a checked-out
working tree) *and* the BoJ REST runtime, which is pending the Elixir
rewrite — until that deploys, the templated `k9iser-regen` trigger is
fire-and-forget exactly like `boj-build.yml` is today.
Loading
Loading