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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
8 changes: 4 additions & 4 deletions .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
{
"name": "synthpanel",
"name": "althing",
"version": "0.1.0",
"description": "Run synthetic focus groups and user research panels using AI personas. Define personas in YAML, design survey instruments, and collect structured qualitative feedback — all from Claude Code.",
"mcp_servers": {
"synth_panel": {
"command": "synthpanel",
"althing": {
"command": "althing",
"args": ["mcp-serve"],
"env": {}
}
},
"skills": [
"skills/synthpanel/SKILL.md",
"skills/althing/SKILL.md",
"skills/focus-group/SKILL.md",
"skills/name-test/SKILL.md",
"skills/concept-test/SKILL.md",
Expand Down
4 changes: 2 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# SynthPanel API Keys (set at least one)
# Althing API Keys (set at least one)
ANTHROPIC_API_KEY=
OPENAI_API_KEY=
GEMINI_API_KEY=
XAI_API_KEY=
OPENROUTER_API_KEY=
# Optional
SYNTHPANEL_LOG_LEVEL=INFO
ALTHING_LOG_LEVEL=INFO
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ body:
id: version
attributes:
label: synth-panel version
description: Output of `synthpanel --version` or the installed package version.
description: Output of `althing --version` or the installed package version.
placeholder: e.g. 0.7.4
validations:
required: true
Expand Down Expand Up @@ -42,7 +42,7 @@ body:
description: Minimal steps to reproduce. Include command, YAML input, or code snippet.
placeholder: |
1. Create `personas.yaml` with ...
2. Run `synthpanel panel run --personas personas.yaml --instrument ...`
2. Run `althing panel run --personas personas.yaml --instrument ...`
3. Observe ...
validations:
required: true
Expand Down
2 changes: 1 addition & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Choose one (matches the auto-tag labels):

- [ ] Tests added or updated
- [ ] `ruff check src/ tests/` passes
- [ ] `mypy src/synth_panel/` passes
- [ ] `mypy src/althing/` passes
- [ ] CHANGELOG.md updated (if user-visible change)
- [ ] Commits signed off with `git commit -s` (see CONTRIBUTING.md)

Expand Down
2 changes: 1 addition & 1 deletion .github/scripts/verify-ci-checks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# concurrency group cancels one of the twin runs — park a cancelled or
# failed run next to the successful same-named run, and those superseded
# runs never turn green. Counting all of them blocked SynthBench v0.3.1
# (PR #334) and SynthPanel's first v1.6.0 attempt, forcing manual tags.
# (PR #334) and Althing's first v1.6.0 attempt, forcing manual tags.
# Only the newest run per (app, name) reflects the commit's real status;
# GitHub's own merge box collapses check runs the same way.
#
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/auto-tag.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,9 @@ jobs:
echo "tag=$NEW_TAG" >> "$GITHUB_OUTPUT"
echo "New tag: $NEW_TAG"

# Sync src/synth_panel/__version__.py (and the rendered landing page,
# Sync src/althing/__version__.py (and the rendered landing page,
# which derives from it) to the new tag BEFORE tagging. pyproject.toml
# uses `version = {attr = "synth_panel.__version__.__version__"}`, so
# uses `version = {attr = "althing.__version__.__version__"}`, so
# the build's package version is whatever this file says at the tagged
# SHA. Without this step, the tagged commit carries the previous
# version string and the resulting sdist/wheel is named after the
Expand All @@ -146,7 +146,7 @@ jobs:
NEW_TAG: ${{ steps.bump.outputs.tag }}
run: |
VERSION="${NEW_TAG#v}"
printf '__version__ = "%s"\n' "$VERSION" > src/synth_panel/__version__.py
printf '__version__ = "%s"\n' "$VERSION" > src/althing/__version__.py
# Keep every committed artifact that embeds the version in
# lockstep. The v1.5.0 manual cut needed three follow-up PRs
# (#488 #489 #490) because only site/index.html was synced
Expand All @@ -170,7 +170,7 @@ jobs:

# Mint a token for the org-owned `dataviking-release-bot` App. The App is
# an Integration bypass actor on the main branch ruleset (see
# dataviking-infra synthpanel.tf), so pushing the bump commit AS the App
# dataviking-infra althing.tf), so pushing the bump commit AS the App
# is allowed — whereas a github-actions[bot] / GITHUB_TOKEN push to main
# is rejected with GH013. This removed the manual "pre-bump the version in
# the PR" workaround (the escape-hatch branch below still works if a PR
Expand Down Expand Up @@ -204,7 +204,7 @@ jobs:
# drop a refreshed file from the bump commit. Root server.json
# is the second descriptor render_server_card.py rewrites —
# omitting it here left it drifted on main and bit PR #583.
git add src/synth_panel/__version__.py site/index.html site/ \
git add src/althing/__version__.py site/index.html site/ \
site/.well-known/mcp/server-card.json server.json
if git diff --staged --quiet; then
echo "Version artifacts already at ${NEW_TAG} (pre-bumped in PR) — nothing to commit."
Expand Down
46 changes: 25 additions & 21 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
run: pip install -e ".[dev,mcp]"

- name: Mypy
run: mypy src/synth_panel/
run: mypy src/althing/

security:
runs-on: ubuntu-latest
Expand All @@ -76,7 +76,7 @@ jobs:
# Tracking: https://nvd.nist.gov/vuln/detail/CVE-2026-3219
# Tracking: https://nvd.nist.gov/vuln/detail/CVE-2026-6357
#
# `--skip-editable` excludes the `synthpanel` package itself (installed
# `--skip-editable` excludes the `althing` package itself (installed
# via `pip install -e .`). The audit is meant to catch dependency
# vulnerabilities, not vulnerabilities in the package being audited —
# and the latter would also fail "not found on PyPI" on release commits
Expand Down Expand Up @@ -108,7 +108,7 @@ jobs:
run: pip install -e ".[dev,mcp]"

- name: Run tests with coverage
run: pytest tests/ -x --ignore=tests/test_acceptance.py --cov=synth_panel --cov-report=term --cov-fail-under=80
run: pytest tests/ -x --ignore=tests/test_acceptance.py --cov=althing --cov-report=term --cov-fail-under=80

coverage:
runs-on: ubuntu-latest
Expand All @@ -128,7 +128,7 @@ jobs:
- name: Run tests with coverage
run: |
pytest tests/ --ignore=tests/test_acceptance.py \
--cov=synth_panel --cov-report=term-missing --cov-report=html \
--cov=althing --cov-report=term-missing --cov-report=html \
| tee coverage-output.txt

- name: Coverage summary
Expand Down Expand Up @@ -220,10 +220,14 @@ jobs:
run: pip install -e ".[dev,mcp]"

- name: Install the real SynthBench harness (GitHub-only)
# Installed after synthpanel so its `synthpanel>=0.2.0` dependency
# Installed after althing so its `althing>=0.2.0` dependency
# is already satisfied by the editable install and pip does not
# pull a published synthpanel wheel over the checkout.
run: pip install "synthbench @ git+https://github.com/DataViking-Tech/SynthBench.git"
# pull a published althing wheel over the checkout.
# NOTE: the dist name is synthbench-eval (the bare `synthbench` name on
# PyPI belongs to an unrelated project); pip >= 25 hard-errors on a
# requested-name/metadata mismatch, so the requirement must use the
# real dist name. The import package is still `synthbench`.
run: pip install "synthbench-eval @ git+https://github.com/DataViking-Tech/SynthBench.git"

- name: Run the submission contract tests against synthbench validate()
run: pytest tests/test_synthbench_contract.py -v --no-cov
Expand Down Expand Up @@ -276,7 +280,7 @@ jobs:
done

clean-install-smoke:
# sy-6kr: prove `pip install synthpanel` (no source checkout) yields
# sy-6kr: prove `pip install althing` (no source checkout) yields
# a working CLI from a fresh environment. The matrix exercises the
# two wheel-from-PyPI agent-onboarding paths (bare wheel, `[mcp]`
# extra); pipx gets its own job below. Failures here surface as
Expand Down Expand Up @@ -321,27 +325,27 @@ jobs:
set -euo pipefail
python -m venv /tmp/smoke
/tmp/smoke/bin/python -m pip install --upgrade pip
WHEEL=$(ls dist/synthpanel-*.whl)
WHEEL=$(ls dist/althing-*.whl)
/tmp/smoke/bin/python -m pip install "${WHEEL}${{ matrix.install.extras }}"

# No source directory is on the path — `synthpanel` resolves
# No source directory is on the path — `althing` resolves
# only via the wheel. If any of these fail, the install path
# is broken for new users.
/tmp/smoke/bin/synthpanel --version
/tmp/smoke/bin/synthpanel --help > /dev/null
/tmp/smoke/bin/synthpanel whoami
/tmp/smoke/bin/althing --version
/tmp/smoke/bin/althing --help > /dev/null
/tmp/smoke/bin/althing whoami

# sy-e28: prove the no-credentials install-only mode works in a
# clean wheel install. Unset the fake key for this step so it
# actually exercises the "credentials missing" path; exit code
# must still be 0 because install_ok dominates.
env -u ANTHROPIC_API_KEY /tmp/smoke/bin/synthpanel doctor --install-only
env -u ANTHROPIC_API_KEY /tmp/smoke/bin/althing doctor --install-only

/tmp/smoke/bin/synthpanel doctor
/tmp/smoke/bin/althing doctor

# `[mcp]` matrix entry must additionally import the MCP server.
if [ -n "${{ matrix.install.extras }}" ]; then
/tmp/smoke/bin/python -c "from synth_panel.mcp.server import mcp; print('mcp server import OK')"
/tmp/smoke/bin/python -c "from althing.mcp.server import mcp; print('mcp server import OK')"
fi

clean-install-pipx:
Expand Down Expand Up @@ -373,13 +377,13 @@ jobs:
set -euo pipefail
pip install --upgrade pipx
python -m pipx ensurepath
WHEEL=$(ls dist/synthpanel-*.whl)
WHEEL=$(ls dist/althing-*.whl)
# `--python` keeps pipx pinned to the runner's 3.14; the default
# would otherwise reach for the system python.
python -m pipx install --python "$(which python)" "${WHEEL}"
# pipx installs to ~/.local/bin by default.
export PATH="$HOME/.local/bin:$PATH"
synthpanel --version
synthpanel --help > /dev/null
synthpanel whoami
synthpanel doctor
althing --version
althing --help > /dev/null
althing whoami
althing doctor
10 changes: 5 additions & 5 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ jobs:
IS_STABLE: ${{ steps.ref.outputs.is_stable }}
run: |
set -euo pipefail
IMAGES=("ghcr.io/dataviking-tech/synthpanel")
IMAGES=("ghcr.io/dataviking-tech/althing")
MAJOR="${VERSION%%.*}"
REST="${VERSION#*.}"
MINOR="${REST%%.*}"
Expand Down Expand Up @@ -148,7 +148,7 @@ jobs:
push: true
tags: ${{ steps.tags.outputs.tags }}
build-args: |
SYNTHPANEL_VERSION=${{ steps.ref.outputs.version }}
ALTHING_VERSION=${{ steps.ref.outputs.version }}
VCS_REF=${{ github.sha }}
BUILD_DATE=${{ steps.ref.outputs.build_date }}
provenance: true
Expand All @@ -163,7 +163,7 @@ jobs:
TAGS: ${{ steps.tags.outputs.tags }}
run: |
{
echo "## Pushed synthpanel ${VERSION}"
echo "## Pushed althing ${VERSION}"
echo ""
echo "**Digest:** \`${DIGEST}\`"
echo ""
Expand All @@ -175,7 +175,7 @@ jobs:
echo ""
echo "**Try it:**"
echo '```bash'
echo "docker pull ghcr.io/dataviking-tech/synthpanel:${VERSION}"
echo "docker run --rm -e ANTHROPIC_API_KEY=\$KEY ghcr.io/dataviking-tech/synthpanel:${VERSION} prompt 'hello'"
echo "docker pull ghcr.io/dataviking-tech/althing:${VERSION}"
echo "docker run --rm -e ANTHROPIC_API_KEY=\$KEY ghcr.io/dataviking-tech/althing:${VERSION} prompt 'hello'"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
20 changes: 10 additions & 10 deletions .github/workflows/publish-test.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
name: Publish Dev to TestPyPI

# Every merge to main publishes a dev build to test.pypi.org.
# Version is auto-generated: {src/synth_panel/__version__.py version}.dev{run_number}
# Version is auto-generated: {src/althing/__version__.py version}.dev{run_number}
#
# ONE-TIME MANUAL SETUP (required before the first successful publish):
#
# 1. On https://test.pypi.org/manage/account/publishing/ add a trusted
# publisher with:
# project name = synthpanel
# project name = althing
# owner = DataViking-Tech
# repo = synthpanel
# repo = althing
# workflow = publish-test.yml
# environment = pypi-test (must match the `environment:` key below)
# 2. In GitHub repo Settings -> Environments, create an environment
Expand Down Expand Up @@ -48,18 +48,18 @@ jobs:
run: |
set -euo pipefail
# pyproject.toml declares `dynamic = ["version"]` and reads the
# version from src/synth_panel/__version__.py, so that file — not
# version from src/althing/__version__.py, so that file — not
# pyproject.toml — is what must be rewritten here. The previous
# pyproject-based sed silently no-oped after the dynamic-version
# migration and every push re-uploaded the same wheel, which
# TestPyPI rejected with "400 File already exists".
BASE=$(sed -n 's/^__version__ = "\(.*\)"$/\1/p' src/synth_panel/__version__.py)
BASE=$(sed -n 's/^__version__ = "\(.*\)"$/\1/p' src/althing/__version__.py)
if [ -z "$BASE" ]; then
echo "::error::Could not read base version from src/synth_panel/__version__.py"
echo "::error::Could not read base version from src/althing/__version__.py"
exit 1
fi
DEV_VERSION="${BASE}.dev${GITHUB_RUN_NUMBER}"
printf '__version__ = "%s"\n' "$DEV_VERSION" > src/synth_panel/__version__.py
printf '__version__ = "%s"\n' "$DEV_VERSION" > src/althing/__version__.py
echo "version=$DEV_VERSION" >> "$GITHUB_OUTPUT"
echo "Dev version: $DEV_VERSION"

Expand Down Expand Up @@ -89,8 +89,8 @@ jobs:
DEV_VERSION: ${{ steps.version.outputs.version }}
run: |
{
echo "## Published synthpanel ${DEV_VERSION} (dev)"
echo "## Published althing ${DEV_VERSION} (dev)"
echo ""
echo "- TestPyPI: https://test.pypi.org/project/synthpanel/${DEV_VERSION}/"
echo "- Install: \`pip install -i https://test.pypi.org/simple/ synthpanel==${DEV_VERSION}\`"
echo "- TestPyPI: https://test.pypi.org/project/althing/${DEV_VERSION}/"
echo "- Install: \`pip install -i https://test.pypi.org/simple/ althing==${DEV_VERSION}\`"
} >> "$GITHUB_STEP_SUMMARY"
Loading
Loading