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
55 changes: 55 additions & 0 deletions .github/ISSUE_TEMPLATE/bug.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# SPDX-License-Identifier: MPL-2.0
name: "Bug report"
description: "Report something that is broken or behaving unexpectedly."
labels: ["bug"]
body:
- type: textarea
id: what-happened
attributes:
label: "What happened?"
description: "Describe the problem. What did you see?"
validations:
required: true
- type: textarea
id: repro-steps
attributes:
label: "Steps to reproduce"
description: "Exact steps that trigger the problem, one per line."
placeholder: "1. ...\n2. ...\n3. ..."
validations:
required: true
- type: textarea
id: expected
attributes:
label: "Expected behaviour"
description: "What did you expect to happen instead?"
- type: input
id: version
attributes:
label: "Version"
placeholder: "e.g. 1.0.0 / commit SHA"
validations:
required: true
- type: dropdown
id: component
attributes:
label: "Component"
description: "Which part of feedback-o-tron is affected?"
options:
- "MCP server"
- "HTTP intake"
- "Synthesis"
- "Channels/dispatch"
- "CLI"
- "Other"
- type: textarea
id: logs
attributes:
label: "Relevant logs / stack trace"
description: "Paste any logs or stack traces. This block is rendered as shell, so no backticks needed."
render: shell
- type: textarea
id: environment
attributes:
label: "Environment"
description: "OS, OTP/Elixir versions, network specifics"
2 changes: 2 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# SPDX-License-Identifier: MPL-2.0
blank_issues_enabled: true
29 changes: 29 additions & 0 deletions .github/ISSUE_TEMPLATE/feature.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# SPDX-License-Identifier: MPL-2.0
name: "Feature request"
description: "Suggest an improvement or new capability."
labels: ["enhancement"]
body:
- type: textarea
id: problem
attributes:
label: "Problem"
description: "What problem are you trying to solve? What is painful or missing today?"
validations:
required: true
- type: textarea
id: proposed-solution
attributes:
label: "Proposed solution"
description: "What would you like to happen?"
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: "Alternatives considered"
description: "Other approaches or workarounds you have considered."
- type: input
id: scope
attributes:
label: "Scope"
placeholder: "e.g. MCP server, HTTP intake, CLI"
38 changes: 26 additions & 12 deletions .github/workflows/elixir-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,24 @@ jobs:
- name: Detect relevant changes
id: detect
working-directory: ${{ github.workspace }}
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
PATTERN='^elixir-mcp/'
# Fail-safe detector (estate required-gate convention): default to
# running the suite; skip ONLY when a successful diff against the
# base tip shows nothing relevant. Uses git (two-dot tree diff, so
# shallow checkouts work) instead of `gh api`, whose non-JSON error
# responses hard-failed this step (#51).
if [ "${{ github.event_name }}" = "pull_request" ]; then
FILES=$(gh api "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" --paginate --jq '.[].filename')
if printf '%s\n' "$FILES" | grep -qE "$PATTERN"; then
echo "relevant=true" >> "$GITHUB_OUTPUT"
if git fetch --no-tags --depth=1 origin "${{ github.base_ref }}" \
&& FILES=$(git diff --name-only FETCH_HEAD HEAD); then
if printf '%s\n' "$FILES" | grep -qE "$PATTERN"; then
echo "relevant=true" >> "$GITHUB_OUTPUT"
else
echo "relevant=false" >> "$GITHUB_OUTPUT"; echo "No elixir-mcp changes — pass-through (required-check shim)."
fi
else
echo "relevant=false" >> "$GITHUB_OUTPUT"; echo "No elixir-mcp changes — pass-through (required-check shim)."
echo "relevant=true" >> "$GITHUB_OUTPUT"; echo "Detector could not compute the diff — failing safe (run=true)."
fi
else
echo "relevant=true" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -92,17 +99,24 @@ jobs:
- name: Detect relevant changes
id: detect
working-directory: ${{ github.workspace }}
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
PATTERN='^elixir-mcp/'
# Fail-safe detector (estate required-gate convention): default to
# running the suite; skip ONLY when a successful diff against the
# base tip shows nothing relevant. Uses git (two-dot tree diff, so
# shallow checkouts work) instead of `gh api`, whose non-JSON error
# responses hard-failed this step (#51).
if [ "${{ github.event_name }}" = "pull_request" ]; then
FILES=$(gh api "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" --paginate --jq '.[].filename')
if printf '%s\n' "$FILES" | grep -qE "$PATTERN"; then
echo "relevant=true" >> "$GITHUB_OUTPUT"
if git fetch --no-tags --depth=1 origin "${{ github.base_ref }}" \
&& FILES=$(git diff --name-only FETCH_HEAD HEAD); then
if printf '%s\n' "$FILES" | grep -qE "$PATTERN"; then
echo "relevant=true" >> "$GITHUB_OUTPUT"
else
echo "relevant=false" >> "$GITHUB_OUTPUT"; echo "No elixir-mcp changes — pass-through (required-check shim)."
fi
else
echo "relevant=false" >> "$GITHUB_OUTPUT"; echo "No elixir-mcp changes — pass-through (required-check shim)."
echo "relevant=true" >> "$GITHUB_OUTPUT"; echo "Detector could not compute the diff — failing safe (run=true)."
fi
else
echo "relevant=true" >> "$GITHUB_OUTPUT"
Expand Down
108 changes: 108 additions & 0 deletions .github/workflows/proofs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# SPDX-License-Identifier: MPL-2.0
# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
#
# Proofs Gate — type-check the Idris2 contract layer (src/abi/) and enforce
# the empty trusted base (no believe_me / postulate / assert_total).
#
# FeedbackOTron.Contract is the verified CONTRACT SPEC for the Elixir
# FormValidator (see ABI-FFI-README.md). This gate keeps it honestly
# compiling: every push/PR touching src/abi/** re-checks the package under
# a pinned Idris2 toolchain (same asdf+Chez install pattern as boj-server's
# proofs gate).
#
# NOTE: this workflow is path-filtered because it is NOT a required status
# check. If it is ever promoted to required, the `on.*.paths` filters MUST
# be removed and replaced with an always-run `changes` detector job (see
# boj-server .github/workflows/proofs.yml and the estate CI conventions:
# a path-filtered required check that never triggers reports as permanently
# "Expected" and deadlocks the PR).
name: Proofs Gate

on:
push:
branches: [main]
paths:
- 'src/abi/**'
- '.github/workflows/proofs.yml'
pull_request:
branches: [main]
paths:
- 'src/abi/**'
- '.github/workflows/proofs.yml'
workflow_dispatch:

permissions:
contents: read

concurrency:
group: proofs-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

env:
IDRIS2_VERSION: '0.7.0'

jobs:
# Cheap, dependency-free gate: the contract layer claims an EMPTY trusted
# base — no believe_me, no postulate, no assert_total anywhere in src/abi.
trusted-base:
name: Trusted-base audit (empty — no axioms)
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Enforce empty trusted base
run: |
set -euo pipefail
found=0
while IFS= read -r -d '' f; do
# Strip `--` and `|||` comments so documentation may mention the
# banned constructs without tripping the audit.
if sed -e 's/--.*$//' -e 's/|||.*$//' "$f" \
| grep -nE 'believe_me|postulate|assert_total'; then
echo "::error file=$f::Unsound construct in $f — the contract layer must stay axiom-free."
found=1
fi
done < <(find src/abi -name '*.idr' -print0)
if [ "$found" -eq 0 ]; then echo "Trusted base is empty."; fi
exit "$found"

typecheck:
name: Idris2 type-check (contract layer)
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

- name: Install build deps (Chez Scheme + GMP)
run: sudo apt-get update && sudo apt-get install -y chezscheme libgmp-dev build-essential

- name: Cache asdf + Idris2 toolchain
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.asdf
key: idris2-asdf-${{ runner.os }}-${{ env.IDRIS2_VERSION }}

- name: Install asdf + Idris2 ${{ env.IDRIS2_VERSION }}
env:
# Ubuntu's chezscheme package installs the interpreter as `scheme`;
# Idris2's bootstrap Makefile reads $SCHEME (defaults to `chez`).
SCHEME: scheme
run: |
set -euo pipefail
if [ ! -f "$HOME/.asdf/asdf.sh" ]; then
git clone --depth 1 --branch v0.14.0 https://github.com/asdf-vm/asdf.git "$HOME/.asdf"
fi
. "$HOME/.asdf/asdf.sh"
asdf plugin add idris2 || true
asdf install idris2 "$IDRIS2_VERSION"
asdf global idris2 "$IDRIS2_VERSION"

- name: Put Idris2 on PATH
run: echo "$HOME/.asdf/shims" >> "$GITHUB_PATH"

- name: Idris2 version
run: idris2 --version

- name: Type-check the contract package
working-directory: src/abi
run: idris2 --typecheck feedback-o-tron.ipkg
30 changes: 16 additions & 14 deletions .machine_readable/6a2/STATE.a2ml
Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
# SPDX-License-Identifier: MPL-2.0
# STATE.a2ml — Project state checkpoint
# Converted from STATE.scm on 2026-03-15
# Updated 2026-04-09
# Updated 2026-07-09

[metadata]
project = "feedback-o-tron"
version = "1.0.0"
last-updated = "2026-04-09"
status = "production"
version = "1.2.0-dev"
last-updated = "2026-07-09"
status = "active-development"

[project-context]
name = "feedback-o-tron"
completion-percentage = 95
phase = "Production"
completion-percentage = 75
phase = "Synthesis Engine + honest reconciliation"

[components]
cli = "100%"
mcp-server = "100%"
mcp-server = "100% (research_feedback, synthesize_feedback, submit_feedback, migration_observe)"
http-intake = "done (Bandit :7722, localhost-only, off by default; boj bug-filing-mcp wire)"
synthesis-engine = "done (this PR: TemplateFetcher, TemplateCache, IntentClassifier, Hydrator, Research, Synthesizer, FormValidator, FormRenderer, TextSimilarity)"
channels-github = "100%"
channels-gitlab = "100%"
channels-bitbucket = "100%"
channels-codeberg = "100%"
channels-gitlab = "90%"
channels-bitbucket = "90%"
channels-codeberg = "90%"
channels-bugzilla = "100%"
channels-email = "80%"
deduplication = "100%"
deduplication = "wired (record/3 called on successful submission; check/1 consulted pre-dispatch and by Research)"
audit-logging = "100%"
network-verifier = "90%"
network-verifier = "70% (real probing; opt-in via Hydrator network_probe; BGP/RPKI partial)"
rate-limiter = "100%"
retry-backoff = "100%"
zig-ffi = "95%"
idris2-proofs = "80%"
zig-ffi = "stub"
idris2 = "contract spec only (Contract.idr), no FFI enforcement"
Loading
Loading