From 61af68a8f74a5058646d366bcfe829be998f8b02 Mon Sep 17 00:00:00 2001 From: Justin Visser Date: Wed, 10 Jun 2026 14:06:41 +0200 Subject: [PATCH] docs: public-release README; ci: GitHub Actions workflow README rewritten for a public audience: what the tool is and is not (the not-validated disclaimer up front), the model with literature references (Holme & Kim 2002; Kempe, Kleinberg & Tardos 2003), honest status checklist, quick start, layout, the generated-types rule, and the prevention-only framing of the subject. CI mirrors the local checks (go test, gofmt, golangci-lint, vue type-check/lint/test/build) and adds a drift guard: go generate must leave web/src/types/ unchanged, so the Go structs stay the single source of truth in fact, not just in principle. --- .github/workflows/ci.yml | 54 ++++++++++++++++++ README.md | 120 ++++++++++++++++++++++++++++++++------- 2 files changed, 153 insertions(+), 21 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ea20758 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,54 @@ +# Runs the same checks as the README's "Checks" section, plus a guard that +# the generated TypeScript types are in sync with the Go structs. +name: CI + +on: + push: + branches: [main] + pull_request: + +jobs: + go: + name: Go (test, lint, generated types) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + - name: Test + run: go test ./... + - name: gofmt + run: test -z "$(gofmt -l .)" + - name: Generated types are in sync + run: | + go generate ./... + git diff --exit-code web/src/types/ + - name: Lint + uses: golangci/golangci-lint-action@v8 + with: + version: latest + + web: + name: Web (type-check, lint, test, build) + runs-on: ubuntu-latest + defaults: + run: + working-directory: web + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: lts/* + cache: npm + cache-dependency-path: web/package-lock.json + - name: Install + run: npm ci + - name: Type-check + run: npm run type-check + - name: Lint + run: npm run lint + - name: Unit tests + run: npm run test:unit -- --run + - name: Build + run: npm run build diff --git a/README.md b/README.md index 0661357..0636938 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,123 @@ # spreadlab -Self-hosted dashboard that runs an agent-based deepfake-spread model live: -change the levers, watch the spread, and (later) search for the best -intervention under a budget. Output is illustrative, not validated. +[![CI](https://github.com/JustinZeus/spreadlab/actions/workflows/ci.yml/badge.svg)](https://github.com/JustinZeus/spreadlab/actions/workflows/ci.yml) -Status: milestone 2, thin API + parity page done. +An agent-based "what if" dashboard for a hard question: when harmful content +(the modelled case: a non-consensual deepfake) starts spreading through a +school year group, where does a limited education budget actually make a +difference? -## Layout +> **Illustrative, not validated.** spreadlab is a planning and discussion +> aid. The model is a deliberately simple social-contagion simulation; its +> numbers are not predictions about real schools, real platforms, or real +> incidents, and must not be used as such. -- `internal/engine/` - the pure simulation engine (no web dependencies) -- `internal/api/` - thin JSON API over the engine -- `cmd/spreadlab/` - the server binary (`-table` prints a CLI comparison) -- `web/` - Vue 3 + TypeScript frontend (Vite) -- `web/src/types/` - TypeScript types GENERATED from the Go structs; never - edit by hand, regenerate with `go generate ./...` +## The idea -## Development +Schools can rarely reach everyone with a prevention program. spreadlab runs +the *same* outbreak in the *same* simulated social network under different +education strategies, so the only thing that changes is who gets educated. +With the default world (120 students, educate 30% of them): -One command (installs frontend deps on first run, Ctrl-C stops everything): +| Strategy | Reached by the fake | +| -------------------------- | ------------------- | +| No program | 82% | +| Educate 30% at random | 58% | +| Educate the 30% best-connected | 6% | + +Same budget, different targeting, an order-of-magnitude difference. Making +that lever visible (and later: searching for the best intervention under a +budget) is the point of the tool. + +## How the model works + +- **Network**: Holme-Kim preferential attachment with triangle closure + (Holme & Kim, 2002), producing the hubs and clustered friend groups of + real social networks; a port of networkx's `powerlaw_cluster_graph`. +- **Spread**: an independent cascade (Kempe, Kleinberg & Tardos, 2003); + each directed edge gets one random forwarding draw, shared across all + scenarios, so strategies are compared in the same world. +- **Education lever**: an educated student still receives the fake but + never forwards it. Strategies: no program, uniform random, most-connected. +- **Determinism**: every source of randomness flows from seeds in the + config; identical configs produce identical results, pinned by tests. + +## Status + +Early development; interface and API are not stable yet. + +- [x] Simulation engine in Go (tested, deterministic, benchmarked) +- [x] JSON API + TypeScript types generated from the Go structs +- [x] Web frontend reproducing the three-scenario comparison from live data +- [ ] Interactive dashboard (controls, network view, spread animation) +- [ ] Single-binary deploy (embedded frontend), Docker image, hosted demo +- [ ] Intervention optimisation under a budget + +## Quick start (development) + +Prerequisites: Go 1.26+, Node 20+. ```sh +git clone https://github.com/JustinZeus/spreadlab +cd spreadlab ./dev.sh ``` -Or manually, in two terminals: +`dev.sh` starts the Go API (localhost:8080) and the Vite dev server +(localhost:5173, proxying `/api`), and installs frontend dependencies on +first run. One Ctrl-C stops everything. Or run the two halves manually: ```sh go run ./cmd/spreadlab # API on localhost:8080 -cd web && npm run dev # Vite dev server, proxies /api to :8080 +cd web && npm run dev # frontend on localhost:5173 ``` -Checks: +`go run ./cmd/spreadlab -table` prints the three-scenario comparison to the +terminal as a quick engine sanity check. + +## Project layout + +| Path | What it is | +| ------------------ | ------------------------------------------------------- | +| `internal/engine/` | Pure simulation engine; no web dependencies | +| `internal/api/` | Thin JSON API over the engine | +| `cmd/spreadlab/` | Server binary | +| `web/` | Vue 3 + TypeScript frontend (Vite) | +| `web/src/types/` | TypeScript types generated from the Go structs | + +The Go structs in `internal/engine/scenario.go` are the single source of +truth for parameters and results. `web/src/types/` is generated from them +(via [tygo](https://github.com/gzuidhof/tygo)); never edit those files by +hand. After changing `Config`, `Result`, or the API response types: ```sh -go test ./... # engine + API tests -golangci-lint run ./... # Go linter +go generate ./... +``` + +## Checks + +```sh +go test ./... # engine + API tests +golangci-lint run ./... # Go linter go test -bench=. -benchmem ./internal/engine/ # benchmark baseline cd web && npm run test:unit && npm run lint && npm run type-check ``` -After changing `Config`, `Result`, or the API response types, run -`go generate ./...` and commit the regenerated files in `web/src/types/`. +CI runs the same checks, plus a guard that the generated TypeScript types +are in sync with the Go structs. -MIT licensed. +## Background + +spreadlab started as the proof-of-concept tool of a university grant +proposal on AI in an open society; the model semantics were ported from the +Python prototype used in that project's pitch. The subject is handled from +the prevention side only: the tool models how harmful content spreads and +what education changes, nothing about creating such content. + +References: P. Holme & B. J. Kim, *Growing scale-free networks with tunable +clustering* (Phys. Rev. E 65, 2002). D. Kempe, J. Kleinberg & E. Tardos, +*Maximizing the spread of influence through a social network* (KDD 2003). + +## License + +[MIT](LICENSE)