No description
  • JavaScript 55%
  • Python 25.9%
  • TypeScript 10.3%
  • CSS 7.5%
  • HTML 1.2%
  • Other 0.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Krzysztof Krystian Jankowski 353671af5c
Some checks failed
check / tests (push) Has been cancelled
M92: night in the head anti-flash fast path (suite caught it) + test_theme fixture covers 4 themes
2026-09-26 19:08:01 +02:00
.gitea/workflows F5: server/test_api.py smoke (8 tests: guard, backlog CRUD/rejections, state shape, F1 dedupe) + run-tests.sh + Gitea Actions workflow 2026-09-24 23:15:53 +02:00
dashboard M92: night in the head anti-flash fast path (suite caught it) + test_theme fixture covers 4 themes 2026-09-26 19:08:01 +02:00
docs/reports M74: PLAN tab — brainstorming board of questions + producer answer flow. Backend: /pod/notes (add|answer|remove, per-project JSON store POD_NOTES_DIR, _NOTES_LOCK, derived answered status, <=10 F-id refs, token gate, state.notes; double-lane judged, lane A cherry-picked). Frontend: PLAN tab (composer: multiline textarea + attach auto-search over uploads w/ Enter-picks/Escape-blur, chips; board: OPEN/ANSWERED derived badges, pre-wrap, answer + F-id links, REMOVE; open-count badge). UX fixes applied: panel chrome, Enter-submit guard, blueprint/whitepaper font+numbering. tests: server/test_api.py 27 OK, dashboard/test_plan.js new, ALL CHECKS PASSED; qa live e2e 7/7; reviewer SHIP; docs AGENTS/README/report. 2026-09-26 02:15:09 +02:00
server M87-BE: watchdog heartbeat — LOOP_SECONDS shared constant, atomic watchdog-heartbeat.json (POD_HEARTBEAT_FILE) each cycle, /pod/state gains tolerant state.watchdog {last_beat, interval}|null; 42+9 tests 2026-09-26 05:10:55 +02:00
.gitignore M73: FILES tab — per-project uploads (.uploads/<project>/F<n>__<name>, high-water ids never reused), POST /pod/upload raw octet-stream 25MB cap + CSRF/content-type gate, GET /pod/file ctype allowlist + nosniff + CSP (html/svg never renderable), POST /pod/files remove, uploads surfaced in /pod/state; dashboard third tab: uploader (input + drag-drop), client-side resize to fit 2048x2048 (png stays png, else jpeg 0.9), rows id/name/size/age/thumb/remove; 9 new python tests + test_files.js helper suite; AGENTS.md/README/.gitignore; double-lane BE judged (A p=0.97), blind SEC double SHIP (avif ctype, .tmp scan exclusion, negative-CL clamp applied) 2026-09-25 12:01:54 +02:00
AGENTS.md M89: producers now know the PLAN tab — injection step 7b (read open notes at run start, answer immediately / parallel fast worker, re-check before final report, reply/409/promote contract, curl headers) + AGENTS.md 6b upgraded to same discipline 2026-09-26 04:45:15 +02:00
index.ts M89: producers now know the PLAN tab — injection step 7b (read open notes at run start, answer immediately / parallel fast worker, re-check before final report, reply/409/promote contract, curl headers) + AGENTS.md 6b upgraded to same discipline 2026-09-26 04:45:15 +02:00
package.json pi-pod-team: POD producer mode — Jev decisions, team archetypes, live dashboard 2026-09-20 21:11:56 +02:00
preview.png clenaup 2026-09-23 23:27:54 +02:00
README.md M85: edge-matrix verified (reply-after-promote, marker intact, concurrent promotes distinct tokens, tombstoned-parent 404, payload shape) + README/AGENTS docs for plan threads and promote 2026-09-26 03:28:22 +02:00
run-tests.sh F5: server/test_api.py smoke (8 tests: guard, backlog CRUD/rejections, state shape, F1 dedupe) + run-tests.sh + Gitea Actions workflow 2026-09-24 23:15:53 +02:00

Pi POD Team

Dashboard

A pi extension that turns the agent into the producer of a POD team — a small delivery crew of specialist subagents. Tasks are classified, routed, executed, judged, and verified by Jev (TypeSafe's System One decision model), while a live Bloomberg-terminal-style dashboard shows everything as it happens.

task ──▶ STAFF ──▶ PLAN ──▶ SPLIT ──▶ TRIAGE ──▶ EXECUTE ──▶ JUDGE ──▶ VERIFY ──▶ REPORT
         Jev picks   4 planners   split-vote    Jev picks     worker(s)    Jev picks    reviewer / qa /
         the team    compete      then one      single or     (double =    the winner   ux / docs
                     4 plans      archetype     double        blind A/B)   if double    follow-ups
                                  per subtask

How it works

  • Jev decides, code executes. Every classification, plan comparison, and result judgment is a single TypeSafe /v1/systemone call — typed answers with calibrated probabilities, no text generation to parse.
  • Team archetypes. Ten specialists (backend, frontend, ui_designer, ux_designer, qa, reviewer, perf, devops, docs, security), each with its own spawn config and brief. Jev picks who owns each subtask.
  • Double & judge. Risky subtasks run as two blind, independent workers; Jev compares the reports and keeps the winner (low confidence → the producer merges manually and says so).
  • Follow-up checks. After execution Jev flags what else is needed: independent code review, tests, performance measurement, UX critique, documentation — each routed to the right archetype.
  • Workers consult Jev too (Jev-first workers). Workers have no pod_jev tool — they reach the same local brain over curl (POST 127.0.0.1:8100/predict with their archetype as actor) for at most two quick checks per task: one approach choice before starting, one done-check against the acceptance criteria before reporting. Busy → retry once after 3s, then decide alone — never block the task. Worker verdicts show on the dashboard JEV DECISIONS feed (fresh and cached answers alike).
  • Live dashboard. Every Jev verdict, task change, and agent spawn is auto-recorded (pi tool hooks → SQLite) into a dense terminal UI with drag-and-dock panels, resizable edges, column sorting, member drill-downs, and a watchdog that auto-closes finished milestones — including an Event Flow timeline of the whole run (below).

Install — start with /pod-setup

  1. Install the plugin:

    pi install git:https://git.cyfrowynomada.com/CyfrowyNomada/pi-pod-team.git
    

    (or clone into ~/.pi/agent/extensions/pi-pod-team/ / point the extensions array in your pi settings at the cloned folder)

  2. Start pi in your project and run /pod-setup — always the first command. It checks the required pi packages (and tells you exactly what to install if something is missing), asks for your TypeSafe API key, starts the local Jev brain, and verifies the dashboard. Idempotent: rerun it after a reboot or whenever something looks off.

  3. /pod <task> — go.

Usage

/pod                 toggle producer mode
/pod <task>          enable producer mode and start the task
/pod-dashboard       open it — up / down / restart control the brain

Then just talk to pi — it plans, staffs, and delivers, announcing every decision with probabilities.

Event Flow panel

A swimlane timeline of the run — time on the horizontal axis, one lane per actor (producer, Jev, each spawned agent), newest events on the right.

  • Edges are heuristic: assignment → spawn, start → done, converging spawns → the Jev decision that judged them, decision → the next producer action. Join windows are conservative; the legend says so.
  • Dot size = fan-out: how many agents an assignment spawned, or how many starts a decision judged. Big dot = big blast radius.
  • Zoom: 15m / 30m / 60m / 180m / 12h / all chips window the timeline; the anchor is the newest event in the data, never the browser clock.
  • Everything is clickable: decisions open the decision drawer, task events open the task drawer, anything else opens the raw event — keyboard included (Tab + Enter).
  • Active dots breathe: events from the last 5 minutes pulse gently (prefers-reduced-motion turns it off).
  • Producer notes are white squares — see Producer notes.

Producer notes

Producers should emit a note for important events and progress milestones — a milestone done, an important decision, a blocker, a handoff. Two ways:

  • pod_emit tool (inside pi): kind "note" with data:{detail:"<what happened>"}.
  • Raw HTTP: POST /pod/event with JSON {"project":"<name>","kind":"note","data":{"detail":"..."}}. The data object is stored verbatim as the event detail (JSON, truncated at 400 chars).

Notes show up in the dashboard's Event Flow panel as white squares on the producer lane — click one to inspect the raw event.

Durable reports (F0): milestones whose subject says report/audit/investigation must leave their report as a repo file and emit pod_emit kind "report" with data {task_id, path} before completion — the extension blocks report-class completions without that evidence, so reports survive the session that wrote them.

Backlog

The BACKLOG tab is an ordered queue of waiting milestones for the project — name, description, age — with promote/move/remove controls and an add form. The producer pops the top entry when the current run's milestones are all done.

Producers (and humans) manage it over HTTP. Successful mutations return the fresh array:

# add an entry (name <= 120 chars, desc <= 2000)
curl -X POST http://127.0.0.1:8100/pod/backlog -d '{"project":"my-app","op":"add",
  "name":"Add OAuth login","desc":"Google + GitHub providers, session reuse"}'

# move an entry to rank 1 (to is 1-based and clamped); op "remove" takes the id
curl -X POST http://127.0.0.1:8100/pod/backlog -d '{"project":"my-app","op":"move",
  "id":"5ecb471d","to":1}'

The list also rides along in GET /pod/state as "backlog". Storage is one JSON file per project under POD_BACKLOG_DIR (default ~/.pi/agent/pod/backlog/) — point the env var at a path inside the project's repo if you want the backlog versioned with the code.

Plan notes

The PLAN tab is a question line to the producer: post what you want investigated (optionally attaching FILES-tab ids), and the producer answers it before its final report. The tab badge counts open — unanswered — notes; REMOVE drops one.

Producers (and humans) manage them over HTTP. Successful mutations return the fresh array:

# ask a question (text <= 5000 chars; optional files = up to 10 upload F-ids)
curl -X POST http://127.0.0.1:8100/pod/notes -d '{"project":"my-app","op":"add",
  "text":"Why did checkout latency jump after the M12 change?","files":["F3"]}'

# answer one (<= 20000 chars, re-answering overwrites) — the producer's half of the contract
curl -X POST http://127.0.0.1:8100/pod/notes -d '{"project":"my-app","op":"answer",
  "id":"N1a2b3c4d","answer":"Cache stampede on price lookup — fixed, profile attached",
  "answer_files":["F7"]}'

The list also rides along in GET /pod/state as "notes" — that is how the producer sees open questions mid-run. Answered status is derived (answer non-empty or answer_files set), never stored. Storage is one JSON file per project under POD_NOTES_DIR (default ~/.pi/agent/pod/notes/).

Threads — every note accepts nested replies (reply-to-reply, server depth cap 6):

# reply to a note or to another reply (text 1..2000 chars)
curl -X POST http://127.0.0.1:8100/pod/notes -d '{"project":"my-app","op":"reply",
  "parent":"N1a2b3c4d","text":"Reproduced on staging — same cache stampede"}'

A note or reply with children cannot be removed (409 has-replies — remove leaves first). Replies ride in /pod/notes responses as "replies" and in GET /pod/state as "note_replies"; on the PLAN tab each note expands to its thread with an inline reply composer.

Promote to milestone — the PROMOTE button on a note (or the API below) turns a discussion into actionable work. The server mints the next free M-token (same vocabulary as the F1 dedupe) and is idempotent: task_id = plan:<noteId>, re-promoting returns the existing target.

curl -X POST http://127.0.0.1:8100/pod/plan/promote -d '{"project":"my-app",
  "noteId":"N1a2b3c4d","dest":"board"}'   # or "dest":"backlog" to queue it

Board promotes create a pending task titled <M-token>: <note text>; backlog promotes append an entry whose description folds in the note's reply thread (≤2000 chars). The note keeps a promoted {dest, id} marker — the badge links to the task drawer or the BACKLOG tab.

Files

The FILES tab uploads images and text files per project — pick files or drag-and-drop them onto the panel. They are stored under .uploads/<project>/ (env POD_UPLOADS_DIR overrides the root) with stable F1..FN ids that are never reused after a remove, so you can say "check F3" to the agent and both sides mean the same file. REMOVE deletes a file from the list; images larger than 2048×2048 are shrunk client-side before upload (PNG stays PNG, everything else becomes JPEG), and files are served back through a strict content-type allowlist — uploaded HTML/SVG is never renderable. The list also rides along in GET /pod/state as "uploads".

The brain (server/)

The decision endpoint and the dashboard backend live in server/ — a small stdlib HTTP service in front of TypeSafe, plus the SQLite tracker the dashboard reads. You normally never touch it: /pod-setup configures and starts it. Manual run:

cd server
export TYPESAFE_API_KEY=<your TypeSafe key>
python3 api.py          # http://127.0.0.1:8100  (dashboard at /dashboard/)

Run it under systemd for a always-on setup:

systemd-run --user --unit=jev-api --property=Restart=on-failure \
  --setenv=TYPESAFE_API_KEY=<key> --working-directory=<this repo>/server .venv-path/python api.py

watchdog.sh / watchdog.py (optional, also as a user service) pings the dashboard when a run stalls, auto-closes milestones on honest evidence, and autopops the backlog: when no producer is running, no milestone tasks are pending, and the backlog is non-empty, it pops the top entry and launches a fresh producer run on it (env POD_AUTOPOP=0 to disable). Add work remotely from the BACKLOG tab and the machine picks it up within 5 minutes. Each loop it also prunes dashboard events older than POD_RETENTION_DAYS (default 30; 0 disables) from the events table — decisions and task rows are never touched.

Two honesty contracts keep the watchdog from lying:

  • Producer liveness is a lock file, not a process scan. The pi extension writes ~/.pi/agent/pod/producer.lock — one-line JSON {pid, run, cwd, ts} — whenever producer mode is on, heartbeating it at most every 60s. This covers interactive operator sessions and headless autopop runs alike. The watchdog calls a producer alive only if the lock is fresh (<30 min), its cwd matches the workspace, and the pid is alive and not a zombie. Stale locks age out; a clean shutdown removes only its own lock.
  • Auto-close is marker-based. A pending milestone M<id> is completed only when its run log contains the machine-readable line POD-FINAL-REPORT: <id> done — the producer appends it as the last action of a delivered milestone. A crashed run leaves no marker: it stays pending and the watchdog posts a one-time "no final report — crashed?" warning instead of falsely completing it.

Remote access (tailscale serve)

The dashboard is exposed to your tailnet at https://*.tailXXXX.ts.net/dashboard/ (tailnet-only, path-scoped via tailscale serve; /pod is mounted the same way).

Invariant: serve must stay path-scoped to /dashboard + /pod — never whole-port (tailscale serve --bg 8100). A whole-port mount would also expose /shutdown (remote kill) and /predict (the API-key proxy) to every device on the tailnet.

Reboot chain: tailscaled is a system service (enabled) and restores the serve config; jev-api / pod-watchdog are user services, so login linger must be on — it is (loginctl enable-linger kj).

API token (POD_API_TOKEN)

When POD_API_TOKEN is set (non-empty), the mutating POST /pod/* endpoints (/pod/event, /pod/remove, /pod/backlog) require header X-Pod-Token matching it (403 otherwise). GETs stay open, and /predict + /shutdown stay open too — remote access to those is already covered by the path-scoped tailscale serve mount above. The dashboard gets the token injected into the served index.html (window.POD_TOKEN) when token mode is on, so the UI keeps working; with the variable unset, served bytes are exactly as before.

To enable for the systemd user units, add the line to ~/.pi/agent/pod/typesafe.env, ensure both units reference that file via EnvironmentFile= (they live outside this repo), then systemctl --user restart jev-api pod-watchdog — a watchdog without the token would drop its backlog ops (it prints a rejection line instead of failing silently).

Repository layout

index.ts            the pi extension: pod_jev tool, /pod command, producer workflow, event hooks
package.json        pi package manifest
dashboard/          the live dashboard (vanilla HTML/CSS/JS, no build step)
server/api.py       Jev bridge + decision cache + POD tracker + dashboard backend
server/watchdog.*   stall/idle watchdog + milestone auto-close
run-tests.sh        python server smoke + dashboard suites (CI: .gitea/workflows/check.yaml)

Requirements

  • pi with the subagents package (workers spawn as real subagents)
  • A TypeSafe API key (Jev) — decisions are online
  • Python 3 with requests for the brain; anything that can serve a file for the dashboard

Status badges on the dashboard

pending muted · in_progress amber · completed green · thinking amber · idle dashed


MIT. Built by a POD team using itself — every milestone in the commit history was planned, judged, and verified by the team this plugin manages.