Open-source decision service

Pointsman sets the switches.

Your systems ask a question. Pointsman decides which track it goes on, or calls a person when it is not sure.

Cloudflare Workers Swappable models REST + MCP OpenAPI MIT
POST /v1/decide/issue-triagerequest
{ "state": { "issue": {
  "title": "Login page is blank",
  "body": "Since this morning."
} } }
200 OKdecision (shortened)
{
  "decision_id": "8c0f…",
  "profile": "issue-triage", "profile_version": 1,
  "answers": {
    "team":   { "value": "backend", "p": 0.9 },
    "urgent": { "value": false, "p": 0.8 },
    "effort": { "value": 0, "p": 0.7 }
  },
  "rule": 0,
  "action": "auto"
}

What it does, in plain words

Many systems receive things that someone must look at first. A resident reports a closed road. A new issue arrives. A deployment seems stuck. Often the case is clear, and the system can act at once. Sometimes it is not clear, and a person should check it. Pointsman makes this first call, and it keeps a record of every call.

  1. It asks fixed questions

    Example: a resident reports a fallen tree on a road. What kind of closure is it? Could someone be in danger? An AI answers each question and says how sure it is, for example "danger: yes, 85 %".

  2. People's rules decide

    People write the rules, for example: "danger 70 % or more: urgent". The AI does not decide alone. The rules can also use facts from public data, for example whether the place is in a flood zone.

  3. It answers, and keeps a record

    The answer is an action, here "urgent". Pointsman saves the questions, the answers and the rule that it used. A person can correct the answers later. So you can see how often the AI was right.

    publishurgentreview

You can try this example in the demo. "Review" means: a person checks it.

Decisions you can check

Many systems make small routing decisions all day: which team, how urgent, wait or stop. Pointsman makes them in one place, the same way every time, and keeps a record you can measure.

Questions with fixed answer types

Yes/no, choice and score questions. Every answer comes with its probability, so you know how sure the model is.

Policy, not prompts

Rules like team.p >= 0.85 turn answers into an action. The policy is plain config, checked when it is loaded, never evaluated as code.

A person when unsure

Below your threshold the action is review. The decision waits in a review queue; the final answer is sent, signed, to the request's callback_url if it gave one.

Versioned profiles

Questions and policy live in YAML profiles with a JSON Schema, validated in CI. Each decision records the profile version and the model.

Feedback and accuracy

People correct decisions; corrections are logged next to the answers. Measure how often each profile version was right before you raise its thresholds.

Swappable models

Models plug in through adapters. Today: Cloudflare Workers AI (clef-flash, clef), optionally through AI Gateway; your own model server, for example a local Strands Decider; and a mock model for development and tests.

Facts from public data

For a place, Pointsman can look up flood zones and the nearest evacuation site (GSI data), and the way around a closed road by car or on foot (Valhalla routing on OpenStreetMap). The AI never sees them; the rules use them, and each decision names its sources.

Decisions that build on others

A profile's requires lists earlier decisions about the same thing, read from the log by id. A final decision can be rules only, with no AI. When a person answered an earlier step, their answer counts.

The chain as a diagram

GET /v1/profiles/{id}/graph draws a chain of decisions as SVG, or exports it as DMN 1.3 for DMN tools such as dmn-js and Camunda Modeler. Pointsman uses DMN's names, not its decision tables.

How it works

One request in, one decision out. The decision log keeps everything needed to explain it later.

  1. Send a state

    Your system posts what it knows: an issue, a deploy event, a form. As JSON or text, to a profile.

  2. Ask the questions

    The profile's model answers its typed questions. If it fails, a fallback model takes over.

  3. Apply the policy

    The rules compare answers and probabilities. The first rule that matches picks the action.

  4. Act

    Your system follows the action, or a person decides.

    autoreviewyour own

Pointsman for …

One engine, many kinds of work. Each use case is a set of example profiles, facts and clients that run today. Use them as they are, or as a start for your own.

Disaster response

Check reported road closures before they go on the public map. Flood zones and the nearest evacuation site come from public data for Japan; the way around comes from a routing server. Urgent and unclear reports go to a person. When people on foot are cut off from an evacuation site, the evacuation check returns alert, and the final decision makes the report urgent. Works with FIWARE through the bridge.

road-restriction-checkevacuation-access-checkroad-closure-final
examples/disaster-response →

DevOps

Is a quiet AWS CloudFormation deploy stuck, or only slow? A GitHub Action asks Pointsman only after the stack has been quiet for a while, and stops the deploy only after two cancel answers in a row. If Pointsman cannot be reached, nothing is stopped.

deploy-progress
examples/devops →

Developer workflow

Which team gets a new issue, does it block work today, how big is it? A GitHub Action labels the issue when Pointsman is sure, and asks a person when it is not. Agents can ask the same profile through MCP.

issue-triage
examples/developer-workflow →

Profiles are config

A profile says what to ask and what to do with the answers. Change a threshold with a pull request, not a prompt.

  • Reviewed like code. Profiles live in your own private config repository and are published on deploy, write-once per version.
  • Validated before they run. A JSON Schema and a validator check every profile in CI.
  • Visible where you want. A profile can be offered to agents through MCP, or kept to the API.

Profile format

profiles/issue-triage.yamlYAML
id: issue-triage
version: 1
title: { en: Issue triage, ja: Issue の振り分け }
description:
  en: Route a new issue to the right team.
  ja: 新しい Issue を担当チームに振り分ける。
model: clef-flash
fallback_models: [clef]
questions:
  - name: team
    type: choice
    instructions: Which team should handle this issue?
    criteria:
      - { value: backend,  description: "APIs, databases" }
      - { value: frontend, description: "Web pages, styles" }
      - { value: docs,     description: "Documentation" }
  - name: urgent
    type: noul
    instructions: Does this block someone's work today?
  - name: effort
    type: score
    instructions: How much work does this look like?
    criteria: ["under an hour", "a few hours", "a few days", "more than a week"]
policy:
  rules:
    - when: "team.p >= 0.85"
      action: auto
  default: review

Ask from anywhere

The same profiles, the same policy and the same decision log, whoever asks.

REST API

Decide, read a decision, send feedback, work the review queue, get a profile's decision graph. Described in OpenAPI; every client has its own API token, limited to the profiles it needs.

openapi.yaml →

MCP for agents

With the Model Context Protocol (MCP), add Pointsman as a connector, and agents call decide instead of guessing. Machines use API tokens; people log in with GitHub and can correct decisions.

AI and agents →

GitHub issue triage

A GitHub Action labels new issues from a decision, using only the labels you list. When Pointsman is not sure, or cannot be reached, the issue gets the needs-triage label (you can choose another) instead.

actions/triage →

Deploy watch

A GitHub Action that watches a CloudFormation deploy and stops it when Pointsman judges it stuck, instead of waiting for a timeout.

actions/deploy-watch →

FIWARE bridge

FIWARE is open-source software for smart-city data platforms. It uses an open data standard, NGSI-LD. The bridge connects such a platform to Pointsman. When data changes, the bridge asks Pointsman. Then it writes the decision back to the platform, so that other apps can use it. It can also create Decision and Task entities (datamodels.jp models), and a person's answer in the platform resolves the review.

bridge →

Get started

Run it locally in a few minutes, with the example profiles and a mock model. No Cloudflare account needed. The mock gives fixed answers; for real answers on your machine, see docs/models.md.

  1. Install

    Node.js 24 and pnpm 12.

  2. Start the Worker

    A local decision log, then the Worker on port 8787.

  3. Ask for a decision

    Create a local API token and send a state to a profile.

  4. Deploy your own

    Copy the config repository template: your profiles, your Cloudflare account, private.

    Deploy Pointsman →
terminalsh
# 1. install
git clone https://github.com/geolonia/pointsman
cd pointsman
pnpm install

# 2. start
pnpm db:migrate:local
pnpm dev

# 3. ask (in a second terminal)
node scripts/tokens.mjs create \
  --client demo --profiles issue-triage --local
export POINTSMAN_TOKEN=pm_…
curl -s localhost:8787/v1/decide/issue-triage \
  -H "authorization: Bearer $POINTSMAN_TOKEN" \
  -H 'content-type: application/json' \
  -d '{"state": {"issue": {"title": "Login page is blank"}}}'

Pointsman is not 1.0 yet: the API and the profile format may still change between 0.x releases. See the release notes, and open an issue if you try it.

Questions people ask

Short answers to questions we got about Pointsman.

Does this need a large AI model?

No. Pointsman asks the AI only a few questions, each with a fixed kind of answer: yes or no, a choice, or a score. A small, low-cost model is enough for that. By default Pointsman uses the clef models on Cloudflare Workers AI. You can also connect a model that runs on your own server.

Do I have to train a model first?

No. You write the questions and the rules, and the model answers the questions as they are. Pointsman does not train models. If you already have a model trained on your own data, you can connect it through a model server, as long as it answers the same kinds of questions: yes or no, a choice, or a score, each with a probability. Each decision keeps the AI's answers next to people's corrections, so you can see how well a model does before you let it decide more on its own.

Isn't this just a model plus a script that acts on its confidence?

That is the idea, yes. Pointsman is the script part, built so that people can read and check it:

  • The rules are config that the city or team writes, not code.
  • When the profile asks for them, facts about the place from public map data are added before the rules run.
  • When no rule matches, the profile's default action applies, often a person's review.
  • Each decision records the answers, the facts, the rule and the profile version.
  • A later decision can build on an earlier one.
How is this different from GIS tools that train models on spatial data?

Those tools help you make a model, for example one that finds buildings in aerial photos. Pointsman does not make models. It takes a model's answers, adds facts about the place, and lets written rules decide what to do. The two can work together: a model trained elsewhere can answer Pointsman's questions through a model server, if it gives the same kinds of answers (yes or no, a choice, or a score, each with a probability).

What happens when the AI is not sure?

Every answer comes with a probability, and the rules compare it with a threshold you set. If no rule matches, the profile's default action applies, usually review: the decision waits for a person. The person's answer is recorded and counts in later steps.