Repository navigation
Releases: geolonia/pointsman
Release list
Pointsman 0.18.0
Hardening of the Actions, the decide request, profile validation, MCP logins, the GitHub connector, the FIWARE bridge, and the GitHub and CodeRabbit facts (#235, #236).
For deployments:
- Move
refinengine.jsonto the commit ofv0.18.0. No migration, no new secrets, no new GitHub App permission or event. - MCP: people are recorded as
github:<account id>instead ofgithub:<login>. Everyone logs in again once (old grants are refused, and refreshing one fails withinvalid_grant). Decisions recorded asgithub:<login>stay as they are and no longer count as the person's own in chains. - GitHub connector: the comment command now names the question:
/pointsman <answer> <commit> <question>, where<question>is the first 8 characters of the decision id. The decision comment and the check run show the full command; a command without the question is refused with a note. Buttons are unchanged. - Decide: a
refwith control characters, line separators or direction marks is refused with400 invalid_request.
What changed:
- deploy-watch and triage Actions: an answer for a test token (
test: true) never counts. deploy-watch does not count itscancel; triage adds only the review label, and itsactionoutput istest. - Decide request:
refmay not contain control characters, line separators or direction marks.replay-profile.mjsshows such characters in stored text (refs, answers, actions, rule text) escaped. - Repeated decide request (
Idempotency-Key): the answer has the review as it is now:resolvedwithfinal_actionandresolved_atonce a person answered,supersededonce a newer decision replaced it. - Profile validation: the check for profiles that require each other in a circle visits each profile once (it was exponential with shared
requires). - MCP login: the OAuth grant keeps the GitHub account id, and the decision owner is
github:<account id>. The login still names the person in feedback (by). - GitHub connector: a comment command must name the question it answers, so it never answers a question that replaced the one the person read. An answer is taken only when the current settings (
answers.profiles) still offer it.copilot_reviewon a pull request with more than 1,000 reviews is recorded as failed for the commit instead of being checked again every 10 minutes. - FIWARE bridge: a retry after a person resolved the review writes it as resolved with their action (the result, the Decision entity, also one left pending by the first try, and the Task). A retry of a superseded decision writes nothing.
github_release: a changedpackage.jsonis also read at the head, and must differ only in its top-level"version"; a dependency namedversionno longer counts.github_checks: two required workflows with the same path from another repository or ref fail the fact (error). The same workflow required by more than one ruleset counts once.- CodeRabbit
paid_for_head: an edited paid-review comment counts from its last edit, and only when its author made that edit. - CodeRabbit
rebase_only: line numbers are compared too. In a file the base branch changed between the two commits, each approved hunk is followed through the base's own change and must land where the head's hunk is; a hunk on lines the base changed, or a base that is not ahead of the old one, gives no value.
Pointsman 0.17.2
FIWARE bridge: a decision a person still checks is always treated as pending (#232).
For deployments:
- Move
refinengine.jsonto the commit ofv0.17.2. No migration, no new secrets, no new GitHub App permission or event. - FIWARE bridge: a route with
tasknow also opens a Task for an action whose review is pending even whentask.actionsleaves it out.
What changed:
- FIWARE bridge: an answer with
review: {status: pending}(an action in the profile'speople_check) counts as checked by a person, whatever the route'sreviewActionssay. The result and the Decision entity getreviewStatus: pending, the final action waits for the person, and a route withtaskopens a Task for it. Before, a route whosereviewActionsleft out that action wrote it as final.reviewActionsnow only adds actions to check (for engines or profiles that report no review); any otherreviewvalue falls back to the old rule. - FIWARE bridge, work orders: a result with
reviewStatus: pendingcan be resolved by a work order also when its action is not in the route'sreviewActions.
Pointsman 0.17.1
Hardening of facts, connectors and Actions (#229).
For deployments:
- Move
refinengine.jsonto the commit ofv0.17.1. No migration, no new secrets, no new GitHub App permission or event. - FIWARE bridge: a profile with a question named like a field of the result (see below) now gets nothing written; rename the question.
What changed:
- GSI facts,
gsi-shelters-flood: a geometry whose box leaves the tile grid (at or near a pole, or at or across the 180° meridian) is refused: the fact is missing (error), and no tile is read. github_bump: a changed lockfile line with a backslash (an escape such as\u0065or\/, in apackage-lock.jsonor apnpm-lock.yaml) givesregistry_changedtrue. Peers in apnpm-lock.yamlversion are taken off in one pass, also when deeply nested; results for usual lockfiles are the same.- CodeRabbit facts: the editors are read with each comment's and review's text; when that text differs from the copy read before (an edit between the two reads), the fact is missing, as for a comment someone else edited.
- GitHub connector: a recorded approval that is not in the review list (which reads up to 1,000 reviews) is read by its id and dismissed if it still stands; when that read fails, the record stays and the next run tries again.
- FIWARE bridge: after a
409on resolving a review, the final action is written and the Task completed only when Pointsman has the review resolved with the same action; a review still pending is tried again (not when its profile version is gone), and one closed as superseded is reported. - FIWARE bridge: an answer whose name, or
<name>Probability, is a field of the result (value,inputHash,decision,reviewStatusand the others, or an NGSI-LD one such asobservedAt) is refused: nothing is written for that decision, the error says which question to rename, and there is no retry. A profile with a question named like this needs it renamed; the entity's next change is then decided again (#228). - deploy-watch and triage Actions: an answer a person still checks (
review: {status: pending}, frompeople_check) does not count: deploy-watch does not count such acancel, and triage adds only the review label.
Pointsman 0.17.0
The GitHub connector can ask Copilot for a review (#220). Nothing changes until a deployment maps a decision action to the new action.
For deployments:
- Move
refinengine.jsonto the commit ofv0.17.0. No migration, no new secrets, no new GitHub App permission or event. - To use it: map a decision action to
{ "do": "copilot_review" }inGITHUB_CONNECTORactions, only where your organization allows Copilot code review for every repository inrepos. Limit how many commits get a review withskip_draftsand a rule ongithub_copilot(for example itsreviewscount), and keep a Copilot budget in the organization. Not yet tried with a GitHub App (only withgithub-actions[bot]): check the first request when you switch it on.
What changed:
- GitHub connector,
copilot_review(#225): requests a review from Copilot with one fixed reviewer and no settings. At most one Copilot review per head commit, whoever asked (another profile or a person; Copilot known by its account id). Done only when GitHub's answer lists Copilot, otherwise shown on the check run as failed and not tried again for that commit. The pull request is read again right before, and nothing is asked if its head moved. Never for a fork's or a closed pull request. When the connector cannot tell whether Copilot was already asked, it asks nothing and looks again 10 minutes later. - Not built from #220: CodeRabbit commands and paid reviews, because CodeRabbit ignores commands from a GitHub App. A person or an agent still posts them.
- Threat model: amended for
copilot_review(Q44 to Q47); re-bound to this release in a separate pull request.
Pointsman 0.16.0
Asking a person inside the pull request (#147): the engine can close a waiting review, the GitHub connector keeps one question per head commit and acts on the person's answer, and, where a deployment switches it on, a person with write access answers from the pull request itself. Nothing changes until a deployment switches answers on.
For deployments:
- Move
refinengine.jsonto the commit ofv0.16.0. No migration, no new secrets. The engine is deployed before the connector, as the config template's deploy workflow does: the connector calls the engine's newget,closeand (for answers)resolve, as theINTERNAL_CLIENTclient over the internal call, soINTERNAL_CLIENTmust list the connector's profiles (as before). With an older engine, over the internal call a run is tried again until the engine is updated; over HTTPS a missing route means the question is decided again. - Without
answersinGITHUB_CONNECTOR, the GitHub App needs no new permission and no new event. A profile that asks a person (people_check) now keeps one question per head instead of opening a new review on every run, and the connector acts on the answer. - To switch answers on: add
answerstoGITHUB_CONNECTOR; give the connector's GitHub App Contents: Read (for the last pusher) and subscribe it to Check run; for the/pointsmancommand also Issues: Read and Issue comment; listpeople_answersin the profiles answered this way; keep the ruleset's "dismiss stale approvals" and "approval of the most recent push"; switch it on only where everyone with write access may approve. Not yet tried against GitHub itself: whether updating a check run with no actions removes its buttons, and whetherrequested_actionevents need the Check run subscription; check both when first switching it on.
What changed:
- Engine (#221):
POST /v1/reviews/{id}/closecloses a pending review assuperseded. Only the client that made the decision may do it: no callback, a later resolve gets 409, and the review leaves the queue.InternalEnginegainsget,resolveandclose, running the same code as the HTTPS routes after the token check. - GitHub connector, a question per head (#221): a decision that waits for a person is kept, per head commit and profile, and read back instead of decided again. The person's answer (
final_action) is acted on with the same guards: an approval only on the unchanged head, once per head. The question is closed and asked anew when what it rests on changes: a push, the title or description, the base, the draft state, the run settings, the mapped actions, or what a required decision says. Closing a question never holds up a run: a failed close is tried again at most once every 5 minutes, up to 10 times within 24 hours. - GitHub connector, answers in the pull request (#222), off by default:
- buttons on the check run, and a
/pointsman <answer> <12 characters of the head>command (only with"command": true); - each answer is checked: by a person with write access, not the author, not the last pusher, on the unchanged head, while the question is still open;
- caps per pull request and clock hour;
- the answerer is recorded as
github:<login> (<id>)and shown as "answered by ", without a mention; - a command whose 12 characters start more than one commit of the pull request is refused.
- buttons on the check run, and a
- Threat model: amended for #221 and #222 (
P-CONNECTOR-ANSWER-AUTHORIZED,P-CONNECTOR-ANSWER-BOUND,P-CONNECTOR-COMMAND-EXACT, the Answerer,R-CONNECTOR-ANSWERS), accepted; it is re-bound to this release in a follow-up, which also covers 0.15.0. - Repository: CodeRabbit reviews only when asked (#219).
Known limits: an answer is picked up on the pull request's next run; a new profile version on the same head does not re-ask an open question; an answer given after the title or description was edited, before the next run, is resolved in Pointsman but not acted on (fails safe); if a run stops between a decision and saving its question, that earlier review is never closed; a closed review and one replaced by a chained decision both read superseded, and the time of a close is not stored; about 10 accounts can use up the hour's answer reads on one pull request; pull requests from forks cannot be answered in the pull request.
Pointsman 0.15.0
Fixes in the GitHub connector and the CodeRabbit facts, found with the threat model's amendment for v0.14.2 (#211). A minor release because a connector setting that loaded until now can be refused.
For deployments:
- Move
refinengine.jsonto the commit ofv0.15.0. No migration, no new secrets, no new permissions. - Check
GITHUB_CONNECTORfirst: a profile withruns.<profile>.once_per_headand an action mapped toapproveorkeep_approvalis now a config error (the connector answers 500 and logs the setting). Removeonce_per_headfrom that profile, or the approving action. The docs already said not to combine them.
What changed:
- GitHub connector,
once_per_headwith an approval (#212, #216): refused. A kept decision is not asked again on the same commit, so it never withdrew its approval after a failed check or a change request. - GitHub connector, its own reviews (#213, #216): a review event about one of its own approvals, or their dismissal, sent by its own bot, no longer starts a run (each one asked every profile again). The connector remembers the ids of the reviews it gave (the newest 50 per pull request). A person's dismissal of its approval, another app's review or dismissal, and an event that comes before the approval's id is recorded still start a run. The run that approves or dismisses reads the reviews again and draws the decision comment.
- CodeRabbit facts,
ignored(#215, #217): CodeRabbit's summary is left out of the description only as a whole block, from its start to its end marker, as the connector already does. A lone start marker no longer hides the text after it. - Docs:
docs/modules.mdsays how the CodeRabbit facts find earlier decisions (#214, #217); the deploy guide covers migrations on updates and connecting the GitHub connector, and the PR-review guide a Dependabot group (#210); Japanese versions of the four English-only pages (#208); the site's layout (#205, #206, #207, #209). - Threat model: bound to
v0.14.2, covering what shipped sincev0.12.0(#211).
Known limit: the decision comment now relies on the approving run's own re-read of the reviews. If GitHub's review decision lagged behind the approval at that moment, the comment's station stays behind until the next event.
Pointsman 0.14.2
A fix for the token script (#202), and the documentation site at https://pointsman.net (#168, #169, #203).
For deployments:
- Move
refinengine.jsonto the commit ofv0.14.2. No migration, no new secrets, no new permissions. No change in behaviour in the engine or the connectors (the MCP tool definitions moved tosrc/mcp-tools.ts, so the site can list them).
What changed:
tokens.mjs rotate --keep-oldprinted a revoke command without--configand withscripts/tokens.mjsas the path. Run as printed in a config repository, it used the default configuration (and could revoke in another KV namespace, so the old token stayed valid) and a path that does not exist there. It now prints the path it was started with,--localor--remote, and--configas given, quoted for the shell.- Documentation site: https://pointsman.net, with the docs under
/docs/in English and Japanese (Starlight), a reference generated from the code (the engine version is added when the site is built, so a release changes no committed page), an AI and agents page withllms.txtandllms-full.txt, and an "About this site" page. Several pages are still short planned pages. The old addressgeolonia.github.io/pointsman/redirects there, keeping the path. - Repository:
pnpm docs:checkandpnpm site:build, a required CI job "Build the docs", the site as a workspace package (docs-site/), and the GitHub Pages workflow now only runs by hand to publish the redirect.
Pointsman 0.14.1
The decision comment's train shows where the pull request really is (#200).
For deployments:
- Move
refinengine.jsonto the commit ofv0.14.1. No migration, no new secrets, no new permissions. - For every deployment with the connector: each run on an open, non-draft pull request now makes one GraphQL read (the review decision and the last 100 reviews), with or without the comment, because check runs draw the line too. A second one only in runs that approve or dismiss.
What changed:
- Approved comes from GitHub's own review decision (
reviewDecision, what the merge box shows under the repository's rules): the train is at "approved" only when it isAPPROVED, so an approval counts exactly where the ruleset counts it (also an AI review service's), and a drive-by approval never does.CHANGES_REQUESTED,REVIEW_REQUIREDand no review rule at all never show it. The caption says "approved by Pointsman on this commit" when the app's own latest review approves the head, "approved on this commit" when someone's does, else "approved". - Reviewed: any review of the current head by someone other than the author (dismissed and pending ones don't count).
- Merged and closed: on
closed, the comment gets one last update, without asking Pointsman: the train at "merged", or "closed without merging". A reopen runs as usual. The connector acceptsclosedevents only when the comment is on. - Filled stations: the train's station and every station before it are filled.
- No names or review text in the comment.
- Fail safe: if the reviews can't be read (an error, a rate limit, a push in between), the run goes on and the train stays where the last read put it on this head, else at "ready". After the app dismissed its own approval, a failed read never shows "approved".
Pointsman 0.14.0
The GitHub connector can show its decisions inside the pull request: one comment, railway style (#195, #196).
For deployments:
- Move
refinengine.jsonto the commit ofv0.14.0. No migration, no new secrets. - The decision comment is off unless
GITHUB_CONNECTORsets"comment": { "profiles": [...] }(1 to 10 profiles, all fromprofiles). Keys of"signals"must be profiles inprofiles; the config check refuses anything else. It needs the connector app's Pull requests: Read and write; the tokens ask for it only when the comment or an action is on. - For every deployment: check-run titles now start with the signal, for example
🟢 approveor🔴 review (shadow). The action word,(shadow)andexternal_idstay, and the details now show the line and the switch.
What changed:
- The decision comment: one comment per pull request, updated in place, never a new one per decision. A table with each chosen profile's signal (always with a word), decision and rule; a track with stations (draft, ready, reviewed, approved, merged) and
▲under the station where the pull request is (draft, ready, or approved while the app's own approval of the commit stands); and per profile a set of points: the facts read or missing, the AI answers, the track taken (action ← rule N, or the default) and the one not taken. It is found again only by its marker together with the app's own bot login; a person's or another app's comment with the marker is never touched. At most one write per run, only when the text changed. When nothing is left to show, an existing comment changes to a short neutral text. - Signals:
GITHUB_CONNECTOR→"signals"maps a profile's actions to red, yellow or green, optionally with a word (1 to 40 letters, digits, spaces and, . ' ( ) -). Defaults:reviewandask-personred;wait,in-progress,queueandretryyellow;approve,passanddonegreen; any other action yellow "decided: "; a decision a person still checks red. - Text art: every rail starts at column 0 and is only box characters up to where its text starts (names, rules and reasons come after the rails), no emoji inside the art, at most 60 columns. In a font that draws box characters two columns wide the rails stay consistent; labels indented with spaces may drift.
- No pull-request text in the comment: profile and action names are checked names, fact reasons are reduced to plain characters, everything sits in code blocks. Test decisions and invalid answers never put anything in it (an approval they withdraw removes its row).
- Failures never stop the run or the actions: a refused write (for example no permission) is noted on the check runs and not tried again until the text changes; a busy or rate-limited GitHub (5xx, 429, a rate-limit 403) is tried again on the next run.
- The check-run summary now cleans the engine's fact reasons the same way, and fact names of 65 to 100 characters are counted (cut) instead of left out.
Pointsman 0.13.1
The GitHub connector checks the engine's answer before it acts, and never acts on a test decision (#193).
For deployments:
- Move
refinengine.jsonto the commit ofv0.13.1. No migration, no new secrets, no new permissions.
What changed:
- The engine's answer is checked once, for both the HTTP adapter and the service binding:
decision_id(a UUID),action(an action name),profileandref(the ones asked for),profile_version,rule, andfacts,reviewandtestwhen present must have the right shape. A decision kept foronce_per_headis checked again before it is reused. An answer that does not fit is an invalid answer. - Test decisions are never acted on: a decision with
test: true(made with a test token) does nothing. - For an invalid answer or a test decision the connector does nothing (no approve,
keep_approval, comment, label or reviewer request), keeps nothing, sends nothing on throughrequiresand schedules no recheck. In a profile that mapsapprove, an approval of the head commit is withdrawn, as for any decision that does not keep it. For a test decision the check run also says to use a token made without--test. The check run is neutral and saysinvalid answer: nothing doneortest decision: nothing done; the log names the field, never its value. It is not retried; the next event asks again. - A 2xx body that is not JSON (HTTP) or not an object (binding) used to be retried; it is now an invalid answer.