Ctrl K
ComunidadGuías…

CHANGELOG.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Changelog

What each release of attest_tag changed, and what, if anything, an upgrade asks of you. Each
[GitHub release](https://github.com/Attest-Tag/attesttag/releases) opens with its section of
this file.

Versions follow [semantic versioning](https://semver.org). Before 1.0 that means:

- A **patch** release (0.1.1) only fixes things and never migrates the database. A deployment
  pinned to its minor version (`ATTEST_VERSION=0.1` in compose's `.env`, `image.tag: "0.1"` in
  the chart) can restart onto one without reading anything first.
- A **minor** release (0.2.0) may migrate the database or change something you have to act on,
  and its section here says which. Migrations apply by themselves at startup and there are no
  down migrations, so back the database up before a minor upgrade: going back means restoring
  that backup.

## 0.3.0 (2026-10-06)

The first release with images: `ghcr.io/attest-tag/attesttag:0.3.0` for linux/amd64 and
linux/arm64, the fix-job worker images beside it, the Helm chart at
`oci://ghcr.io/attest-tag/charts/attest-tag` and binaries for Linux and macOS, all signed with
cosign. Pin the minor (`ATTEST_VERSION=0.3`, or `image.tag: "0.3"`) to take its fixes and no
migration you did not read about. From here on these are built from `main` only: the release
workflow refuses a tag that `main` does not contain. → [Deploy](guide/deploy.md)

**Code review shows on the pull request while it works.** → [The reactions and the check](guide/code-review.md#the-reactions-and-the-check)

- **Eyes, then a rocket.** A live review puts an eyes reaction on the pull request as it starts and
  swaps it for a rocket once it is posted; a review that fails or is cancelled only takes the eyes
  off.
- **An *attest_tag review* check.** In progress while the review runs, then a success with the score
  and the open findings — or neutral, skipped or cancelled when it was not posted, never a failure,
  so requiring it blocks nothing. It needs the App's **Checks: Read and write** permission, which is
  optional: an installation without it gets the reactions alone.
- **Listed among the reviewers.** A first review with nothing to say inline now posts a review of
  one line — the commit, the score and where the findings are — so the App appears under the pull
  request's reviewers; later runs with nothing new inline still post none.
- **A fix job's pull request is reviewed.** A pull request the App opened itself is no longer skipped
  as a bot's: it is reviewed once somebody marks it ready, as anybody's draft is.

**Code review reviews the bots you name.** A pull request a bot opened was always skipped. *Bots to
review*, a new list in the review settings, names the bots whose pull requests are reviewed without
anybody asking — by login, with or without `[bot]`, or a glob, and `*` for every bot — and adds up
from the connection down like the other lists. Any other bot's pull request is still skipped, and
the authors to skip still apply. → [Settings and their defaults](guide/code-review.md#settings-and-their-defaults)

**Fix jobs say a check did not finish, not that it failed.** A suite or a build the worker's sandbox
killed partway — most often out of memory, on a large repository — was reported as failing: the
pull request opened with "Tests still fail after this change", its table counted one failure, and
the coding agent was told the suite failed before its change, "that may be the bug". It is now
*did not finish (killed: out of memory)* everywhere — the pull request, the thread, the console and
the agent's brief — and the headline also says whether a failing check passed before the change,
fails worse after it, or was failing already. → [Fix jobs](guide/fix-jobs.md)

**Fix jobs check their change with a narrower run.** The coding agent was told to run the whole
build, lint and test suite itself before it stopped, although the harness runs them again after it.
On a large repository, one job spent some 60 of its 150 turns running them, and a type check, again
and again, each turn re-sending the whole conversation so far. The brief now gives each check's
time from before the change and asks for the narrowest run that covers the change — the tests for
the files it touched, the linter on just those files. It runs a whole command only when it passed
in under two minutes, and spends no turns making a command run that the sandbox stops.
→ [Fix jobs](guide/fix-jobs.md)

**A member who is not a Slack admin can no longer disconnect their workspace by pressing Add to
Slack.** Slack hands back the same bot token for an app already installed in a workspace, and a
refused install (the person is not a workspace admin) gave that token back to Slack — revoking the
one the organisation that installed the bot was running on, and taking the bot out of the workspace
for everybody in it. A refused install now gives a token back only when no organisation here uses
that workspace; on one another organisation holds, it says so and points at an invitation. Signing
up with Slack from a workspace another organisation already connected no longer carries straight on
to Add to Slack either: the setup walk shows the workspace is taken and offers to ask for an
invitation. And Slack's `tokens_revoked` disconnects a workspace only when it names the bot's token,
not when people give back tokens of their own. A workspace this already disconnected needs one
reinstall by a Slack workspace admin from the organisation that owns it.

**A review finding can be fixed on the pull request.** Somebody who can push to the repository
ticks the box on a finding's comment, replies `@<app> fix` in its thread, or comments `@<app> fix`
(or `@<app> fix p1`) on the conversation, and a fix job commits the change to the pull request's own
branch — one more commit, pushed as the App, never forced, no new pull request — then answers with
the commit and what the checks said, and the review of the new head closes what it fixed. A change
that breaks a check that passed before it is not pushed; its diff comes back with the answer. Pull
requests from forks are refused, and so are people GitHub says cannot push. *Fixes* is a new review
setting, on by default; turning it back on needs Manage connections. A reaction cannot ask for a
fix: GitHub never delivers reactions to an App.
→ [Fixing a finding](guide/code-review.md#fixing-a-finding-on-the-pull-request)

**The fix-job worker on Google Cloud gets 8 GiB**, up from 4, beside its 2 CPUs: Cloud Run keeps a
job's files in memory, and a large JavaScript repository's suite and build did not fit. Run
`deploy/gcp/worker.sh` again to apply it (`MEMORY=4Gi` keeps the old size). The other platforms'
defaults are unchanged.

**Fix jobs keep their prompt cache, are billed exactly, and stop at their budget.** A coding agent
re-sends its whole conversation every turn, so a long job reads millions of input tokens, nearly all
of them the same prefix again. → [What a job costs](guide/fix-jobs.md#what-a-job-costs)

- **A model proxy in the worker.** The engine calls a proxy on the worker's loopback instead of the
  provider, with a token for that port: the model key no longer enters the sandbox. On OpenRouter
  every call carries the job's `session_id`, which keeps the job on the provider holding its cache,
  and asks for usage accounting, so each answer says what it cost, what came from cache and who
  served it — the real bill, even on the shared key. At the job's budget the proxy stops the engine
  and what it changed is opened as a draft.
- **Providers on purpose.** A new `worker_providers` setting (Settings → Workers → Providers) names
  the OpenRouter providers a job asks for, in order.
- **The cache share everywhere the cost is.** The cached part of the input is stored on the job and
  shown in the Slack report ("9.8M in (90% cached) / 74k out"), the console's job page, the reply on
  a pull request a fix was pushed to, and a new **Cost** section in the pull request a job opens.
- **pi as a second coding agent** (Settings → Workers → Coding agent). Seven tools, a short system
  prompt and its own compaction; on the fix-job evals it read about a sixth of the tokens Qwen Code
  did for the same correct fix. → [Engines and long jobs](guide/fix-jobs.md#engines-and-long-jobs)
- **Long jobs carry less.** Both engines compact against a 160,000-token window instead of the
  model's million; Qwen Code runs without its sub-agent, web and memory tools and cuts tool output
  at 6,000 characters. The brief lists the files the request names as they are in the clone, and
  tells the engine to batch reads and read each file once.
- **Node builds get the memory the container has.** Repository commands run with a V8 heap of about
  60% of it, where a large front end's build ran out of Node's default.
- **Fix-job evals.** `evals/fixjobs` runs small tasks as real jobs and grades them with hidden tests,
  to compare engines, models and providers on what was right and what it cost.
  → [Fix-job evals](evals/README.md#fix-job-evals)

**Models that will not be told which tool to use still answer.** Some endpoints refuse a
`tool_choice` other than `auto`. Meta's Muse models refuse both `none` and a named tool. Claude
Sonnet and Opus 5.5 and Qwen 3.8 Flash refuse a named tool while they reason, and OpenRouter answers
a 404 when no endpoint of a model takes `none` (Llama 4 Maverick). A run on one of them failed at
the step that asked: the first round of "check our docs" or "search the web", the landing round of
a routine or reply that had spent its budget, and the last round of a code review. The request is
now sent again without the choice, which every caller already handles: a tool the model skipped is
run for it, and a call made while landing is dropped. The refusal is remembered per model and per
kind, so a model that takes `none` keeps it.

**A provider that fails partway through is retried, not reported as "no choices".** When an
upstream times out after OpenRouter has started its reply, the failure arrives as a 200 with an
error in the body. That turned into "model returned no choices" and was never retried. It is now an
error carrying what the provider said, and a timeout, a rate limit or a 5xx is tried once more.

**Google Gemini and DeepSeek as your own model key, and Gemini 3's tool calls on Google's own
endpoint.** Settings → Models → *Your model key* offers Google Gemini and DeepSeek beside OpenAI and
OpenRouter, each with its address filled in. → [Its own model key](guide/plans.md#its-own-model-key)

- **Tool rounds go back as the endpoint wrote them.** Gemini 3 at Google's own endpoint refused
  every round after a tool call ("Function call is missing a thought_signature"), so any question
  that needed a tool failed there. A tool round now carries back what the endpoint handed out with
  its calls: the signature Google puts on each one, OpenRouter's `reasoning_details` (Gemini's
  signatures there) and DeepSeek's `reasoning_content`. OpenAI's API writes none of these, and is
  sent none.
- **Model lists and prices.** Google's list drops the `models/` it puts in front of every id. A
  Gemini model is priced from the catalogue as `google/<id>` and a DeepSeek one with a version in its
  name as `deepseek/<id>`; DeepSeek's unversioned names (`deepseek-flash`) are aliases and stay
  unpriced.
- **DeepSeek has no embedding model**, so document search is off while a DeepSeek key is in use. Its
  models refuse a named tool while they reason, which the change above already recovers from.

**A custom review type can be deleted** once no branch rule names it. The row and its versions are
kept (migration `0033_review_type_deleted` marks it), so past runs still name what they ran with
and its key stays taken; a built-in type or its copy cannot be deleted. → [Code review](guide/code-review.md)

Upgrading: migrations `0032_job_cached_tokens` and `0033_review_type_deleted` add columns and apply
themselves at startup; both are additive, so the old version keeps working while the new one
rolls out. The fix-job proxy, pi, and fixes on the pull request need the worker image from this
release (`deploy/<platform>/worker.sh`, or the published `attesttag-worker:0.3.0`): an older worker
records no cached tokens and refuses a fix-on-the-pull-request job at its clone. For the code review
check, add *Checks: Read and write* under the App's *Permissions & events* at GitHub; each account
that installed it is then asked to accept, and its next review has a check.

## 0.2.0 (2026-10-04)

Like 0.1.0, the source alone: no images, Helm chart or binaries are published for it, so build from
the checkout as the deploy guides say.

**Code review.** attest_tag reviews pull requests on GitHub, through the GitHub App it already
installs. → [Code review](guide/code-review.md)

- **One review per run, as a comment.** Inline findings, each checked by Go against the code and,
  when a model raised it, by a second model call that tries to refute it, and one summary comment
  edited in place, with a confidence score from 0 to 5 that Go computes from the open findings. It
  never approves or requests changes, and never runs the pull request's code.
- **Settings by connection, group and repository**, under Automation › Reviews. An installation
  added there starts in shadow, which records the whole review in the console and writes nothing to
  GitHub. A single value comes from the nearest level that sets it; lists add up.
- **Review types and branch rules.** General, Security, Tests, Performance and Release summary ship
  built in; a team can edit them, add its own, keep every version, and try an edit on a pull request
  before saving it. Ordered rules on the base and head branch choose which types a pull request
  gets, and label rules add types for a label on it — put on later, a label reviews what it adds.
- **Skills.** A type can follow up to five skill folders — a `SKILL.md` and the Markdown beside it —
  from the repository under review (read at the pull request's base), one of the organisation's
  connected repositories, or any public repository, pinned to a commit if you like; a finding that
  rests on one cites it, and the summary names the skills the review followed.
- **Commands and replies.** `@<app> review`, `full review`, `status`, `help`, `pause` and `resume`
  from the repository's own people, and a verdict on every reply in a finding's thread, which can
  withdraw or downgrade the finding within limits on who may move a P0 or P1. A finding that is
  fixed or withdrawn has its thread resolved on GitHub where the App's token is allowed to (best
  effort; the connection card says when GitHub refuses). Automatic reviews of one pull request pause
  by themselves after five, and the summary says so.
- **Re-reviews close what a push fixed.** Each later review checks the earlier findings at the new
  head: a fixed one moves to the summary's *Fixed* with *Fixed in* in its thread, one whose code is
  gone to *Outdated*, and a "fixed in" claim is checked rather than taken at its word.
- **Start review** in the console, with an estimated cost, and **Run again** on a past review. A
  paused pull request says *Paused* there, with **Resume**; a branch rule takes **Labels**, and reads
  `label perf → +Performance`; and where the plan has no code review the Reviews page says which plan
  has it, with the way to upgrade, while `CODE_REVIEW=off` takes the page out of the console.
- **Reviews asked for in Slack or Teams.** "review acme/web#12" in a channel starts one through the
  bot's GitHub tools, for a repository that channel is granted in Access bundles and nothing else,
  under the same gate, budgets and throttles as the console's Start review. → [Code review](guide/code-review.md#from-a-slack-or-teams-channel)
- **Announcements in a chat channel.** A level's *Channel*, or a branch rule's *Notify*, names a
  Slack or Teams channel the bot is in; each pull request gets one message there, edited as reviews
  finish and when it is merged, with each of those a reply in its thread. Choosing it needs
  `connections.manage`, and a channel shared with another organisation (Slack Connect) is never
  posted in.
- **Starts, failures and Notify on.** A review's start edits the message to *Reviewing `abc1234`…*
  with no reply and without holding the review up; a failed review puts it back and says which kind
  of failure, never the error; a review the budget, a pause or the plan stopped is said once. *Notify
  on* picks which of starts, results, failures and merges a channel hears, inherited whole, and
  needs only `reviews.manage`. A console link that names a settings node opens on Settings.
- **Adding and removing repositories.** *Add repositories…* on a connection lists what its
  installation reaches at GitHub and saves the ones ticked; *Remove from reviews…* takes one out of
  code review while its connection stays for Slack tools and fix jobs, and *Restore* puts it back.
- **The console assistant on Reviews.** With a type or a level open, ask it to add, change or switch
  off a type's rules, make a new type, or add, change, move or remove a level's branch rules. It
  proposes a card and writes nothing; Confirm makes the page's own save under the person's own
  permissions, and is refused with nothing written if the type, the type a new one copies, or the
  rules changed since; the page then reads again and shows the change that won. It names who wrote
  a rule learned on GitHub, while the rule keeps their words, and which skills a type follows; skills,
  models, budgets and posting stay on the page. A card is confirmed only in the organisation it
  was proposed in, draws every path pattern it adds or removes and the model and budget a copy
  brings, and says when Confirm uses your `connections.manage`; Activity shows the assistant's
  tool-call arguments only to a reader who also holds `audit.view` and what the call was about,
  and only such a reader may search what it was asked.
  The assistant is offered to anyone with `reviews.view` and `reviews.manage` now, as well as to
  channel and approver admins, and what it is told about code review is sent from the Reviews page
  only. → [Changing types and rules from the assistant](guide/code-review.md#changing-types-and-rules-from-the-assistant)
- **A settings save never undoes a newer one.** If somebody saved a level's rules, or the rules it
  inherits, since the page read them, Save is refused and the console keeps your edits over theirs;
  through the API, `digests.branch_rules` sent back as `expect` does the same. A save of any field
  that lands while another save of the level is being checked refuses that one too, rather than
  being put back by it.
- **Money of its own:** a maximum per review, a monthly review budget and a daily cap, each held
  before a review starts, and a *Code reviews* switch on an organisation's own model key.
  `CODE_REVIEW` says which organisations have it at all: every one (`all`, the default), those on
  the pro or enterprise plan, the enterprise plan only, or none (`off`).
- **The one write that never goes through Confirm.** A review on a repository set to *Live* is
  posted with nobody pressing anything, and no allow rule or `auto` setting is involved; *Live*, the model and the money need `connections.manage`.
  [Guardrails](guide/security.md#code-review-posts-without-a-confirm) says what stands in for the
  Confirm.

**Security fixes**, found by an audit of the code review change, of which the first two were in
0.1.0 too:

- Attaching a GitHub App installation to an organisation now needs its account's owner, or somebody
  with admin on every repository it covers. It used to need only that the installation was visible
  to the person, so an organisation's member or outside collaborator could attach one nobody had
  claimed yet, with its tokens.
- A member without `audit.view` can no longer search the console assistant's questions and replies,
  which told them, a guess at a time, what a redacted reply said.
- A label rule no longer hides a branch-rule change from the check that keeps live posting with
  `connections.manage`.

What an upgrade asks of you:

- Five migrations, `0027_code_review`, `0028_review_lane`, `0029_review_snippet`,
  `0030_review_notify` and `0031_review_type_skills`, all additive and applied at startup.
  Back the database up first, as for any minor release.
- Nothing more, unless you want code review. Then set `GITHUB_APP_WEBHOOK_SECRET`, turn the App's
  webhook on at GitHub with the five events
  [configuration](guide/configuration.md#the-apps-webhook-and-permissions-for-code-review) lists,
  and add an installation under Automation › Reviews.
- Connecting a GitHub App installation (Connect GitHub, or the install redirect) takes the account's
  owner or an admin of every repository it covers; somebody who only requested the install is told
  to ask one of them. Installations already connected are not affected.
- Two new permissions: `reviews.view`, which the viewer, editor and admin roles hold, and
  `reviews.manage`, which editor and admin hold. A role you made yourself has neither until somebody
  ticks them.

## 0.1.0 (2026-09-28)

The first release. It is the code that has run one organisation's Slack for months and runs the
hosted service, with everything that was specific to that deployment turned into configuration.

It is the source alone: no images, Helm chart or binaries are published for it yet, so build
from the checkout as the deploy guides say (`docker build -t attesttag-local .`, or
`make build`).

- **Answers in Slack and Microsoft Teams**, in threads and DMs, on open-weight models through any
  OpenAI-compatible endpoint (OpenRouter and GLM-5.3-Flash by default).
- **Tools with no setup:** Slack history and search, your documents, web search and fetch, memory
  per channel or per person, and routines it schedules when asked.
- **Connections without handing over a key.** Credentials for thirty-odd services and remote MCP
  servers are sealed at rest and injected by a proxy at the network edge, so the model never
  sees one, and every write waits for somebody to press Confirm.
- **Fix jobs:** an isolated worker clones a repository, makes the change, runs its tests and
  opens a draft pull request. It never merges.
- **An admin console** for scopes, connections, documents, routines, activity, the audit log and
  budgets, with password, Slack, Microsoft or OpenID Connect sign-in and two-factor.
- **A `/v1` API and an MCP server**, with keys that carry exactly their maker's access.
- **SQLite or Postgres.** SQLite by default, replicated to any S3-compatible bucket if you give
  it one; Postgres when you want more than one instance.
- **Runs anywhere a container does:** Docker Compose, a Helm chart, and scripts for Cloud Run,
  ECS Fargate and Azure Container Apps. Or run the binary itself.

What is still rough, and what is wanted next, is in the README's
[Status](https://github.com/Attest-Tag/attesttag#status).