com.linebreakapp/linebreak-gate
This server exposes an MCP interface to the LineBreak approved-spec workflow: browse and manage story state, inspect acceptance criteria, and check work against the same gate engine used in CI.
list_stories – list all approved stories with id, title, epic, local status, and acceptance-criteria counts.
get_story – fetch one approved story in full, including its acceptance criteria and check types, to use as the definition of done before implementing.
next_story – get the next approved story that is not yet done, based on local story state.
set_story_status – record local story progress (doing/review/done) with an optional comment; never mutates external trackers or approved criteria.
check_story – run a story's acceptance criteria against the working tree using the same engine as the merge gate, returning pass/fail/needs-signoff per criterion.
spec_status – check the approved spec bundle's version, approver, story count, and offline verification of the approval signature.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@com.linebreakapp/linebreak-gatelist all approved stories for this sprint"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
linebreak-gate — the LineBreak security gate at the git/CI boundary
See it run
A real pull request, blocked for real: the gate is a required check, so the merge button goes gray until the CVE is fixed or a named human records an override.

See it live — a public PR you can open right now →
A real recording, no mock: the scan blocks a critical CVE fail-closed, the pin gets fixed, the gate opens.

The spec loop: a named human approves the criteria, check blocks until the manual criterion carries a sign-off, then everything passes.

Blocks merges that carry known vulnerabilities. One tool, two detectors — dependency scanning is free; the AI review is the Pro upgrade:
Dependency CVE scan — free, no key — osv-scanner across every ecosystem (npm, PyPI, Go, Cargo, Maven, …), with an
npm auditfallback for npm projects (npm-only coverage and no installed-version data — the GitHub Action fails closed if osv-scanner can't be installed instead of degrading to it).AI SAST — Pro — an LLM security review of first-party source (injection, broken auth, secret exposure, SSRF, unsafe deserialization, crypto misuse) with adversarial verification, enabled by
LINEBREAK_LICENSE_KEY(hosted, uses credits) orANTHROPIC_API_KEY(your own key, takes precedence). Without a key the dependency scan still runs and this pass is skipped with a notice.
The gate blocks and can propose; it never auto-clears on an agent's say-so. A human approves the fix or records an override — with a reason and an approver — in a git-committed audit file.
This is the same scanner core that powers the rest of LineBreak's in-product security gate (the desktop backend imports this package), but it is fully standalone: a team that has never touched anything else from LineBreak can add the gate to their repo and get real enforcement.
Contributing & license. This repo is the published source of
linebreak-gate(Apache-2.0): every release lands here and on PyPI from our CI, and every change passed our own gate first — CVE scan and human-approved criteria, the same discipline we sell. Bug reports and feature requests: open an issue or discussion here; we read everything. Direct PRs to this repo can't be merged (releases flow through our review pipeline), so start with an issue and we'll take it from there.
Related MCP server: backlog-mcp
Quickstart — GitHub Actions
# .github/workflows/security-gate.yml
name: Security gate
on:
pull_request:
permissions:
contents: read
pull-requests: write # for the summary comment
jobs:
gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: Baktun-Studio/linebreak-gate@v1
with:
# fail-on: high # blocking floor; default: critical
# Optional today; required once license enforcement is enabled.
license-key: ${{ secrets.LINEBREAK_LICENSE_KEY }}
# Enables the AI code review; leave unset for dependency scan only.
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}The action runs linebreak-gate scan, always runs report, posts one PR
comment (updated in place on every push, never spammed), uploads the JSON
report + audit artifacts as a workflow artifact, and fails the check per the
scan's exit code.
Make it a real boundary: require the check
A CI job that can be ignored is a dashboard, not a gate. In your repo:
Settings → Branches → Branch protection rules → your default branch →
"Require status checks to pass before merging" → add the gate job (the
name of the job that runs this action). From then on a PR carrying a critical
CVE cannot be merged through the GitHub UI.
Quickstart — any other CI (GitLab example)
The CLI is a plain Python package with strict exit codes — 0 pass, 1
blocking findings, 2 tool/config error (fail closed: a scanner crash
fails the pipeline, it is never a clean pass). Any CI that respects exit codes
gets the same enforcement:
# .gitlab-ci.yml
security-gate:
image: python:3.11
script:
- pip install linebreak-gate
- curl -fsSL -o /usr/local/bin/osv-scanner
"$(curl -fsSL https://api.github.com/repos/google/osv-scanner/releases/latest
| python -c "import json,sys;print(next(a['browser_download_url'] for a in json.load(sys.stdin)['assets'] if a['name'].endswith('linux_amd64')))")"
- chmod +x /usr/local/bin/osv-scanner
- linebreak-gate scan
- linebreak-gate reportMark the job as required (no allow_failure) and protect the branch.
Bitbucket Pipelines y Azure DevOps
The same gate on Bitbucket Pipelines and Azure DevOps: linebreak-gate ci
runs scan + check, posts the PR comment and the build status through the
provider's API, and exits 0/1/2. This section is in Spanish for the teams
piloting it; the step-by-step runbook is docs/RUNBOOK_BITBUCKET_AZURE.md
in the monorepo.
La compuerta es la misma en cualquier CI. El comando linebreak-gate ci hace
en un solo paso lo que la Action de GitHub hace en varios: corre el escaneo de
dependencias y la revisión de código con IA (si hay llave), evalúa los
criterios de aceptación aprobados con el alcance correcto (por historia en el
pull request, todo el paquete en la liberación), deja la evidencia en
.linebreak/ci-out/ (report.txt, criteria.txt, report.json,
comment.md y los registros de auditoría) para publicarla como artefacto,
publica un comentario en el pull request (actualizado en cada corrida,
nunca repetido) y un estado de build, y termina con el código 0 (pasa), 1
(bloquea) o 2 (error de herramienta: la compuerta queda cerrada). El
comentario tiene el mismo contenido que el de GitHub.
Sin credenciales de API, el veredicto se imprime igual, el comentario y el estado se omiten con un aviso, y el código de salida sigue bloqueando el pipeline. La compuerta nunca se abre por no poder comentar.
linebreak-gate init detecta el proveedor por el remoto de git y escribe el
archivo que corresponde; --provider bitbucket|azure|all lo elige a mano.
Los dos archivos que genera son exactamente los de abajo.
Bitbucket Pipelines
# bitbucket-pipelines.yml
# Python image with git and curl; the gate pins osv-scanner itself.
# Alternative: the gate's CI image built from packages/gate/Dockerfile.ci
# (osv-scanner preinstalled), pushed to a registry your workspace can pull.
image: python:3.11
definitions:
steps:
- step: &linebreak-gate
name: LineBreak gate
script:
# osv-scanner drives the dependency scan; without it the gate fails closed.
- curl -fsSL -o /usr/local/bin/osv-scanner https://github.com/google/osv-scanner/releases/latest/download/osv-scanner_linux_amd64
- chmod +x /usr/local/bin/osv-scanner
- pip install --quiet "linebreak-gate>=1.13.4,<2"
# Scan + acceptance criteria, PR comment and build status, exit 0/1/2.
# Repository variables (Repository settings > Pipelines > Repository variables):
# LINEBREAK_LICENSE_KEY optional today; required once enforcement is enabled
# ANTHROPIC_API_KEY enables the AI code review (secured)
# BITBUCKET_ACCESS_TOKEN repository access token, scopes pullrequest:write
# and repository:write, for the PR comment and status
- linebreak-gate ci
artifacts:
- .linebreak/ci-out/**
pipelines:
pull-requests:
"**":
- step: *linebreak-gate
branches:
main:
- step: *linebreak-gateVariables del repositorio (Repository settings > Pipelines > Repository variables, marcadas como secured):
LINEBREAK_LICENSE_KEY: opcional hoy; requerida cuando se active la exigencia de licencia.ANTHROPIC_API_KEY: habilita la revisión de código con IA; sin ella corre solo el escaneo de dependencias, con aviso.BITBUCKET_ACCESS_TOKEN: token de acceso del repositorio (Repository settings > Access tokens) con permisospullrequest:writeyrepository:write. Es lo que permite el comentario y el estado de build. Alternativa:BITBUCKET_USERNAME+BITBUCKET_APP_PASSWORD.
Protección de rama equivalente a "required check" (Repository settings >
Branch restrictions, rama main): el merge check "Check the last commit for
at least 1 successful build and no failed builds". Los merge checks son parte
de Bitbucket Cloud Premium; con el plan Standard el build rojo se ve en el
pull request y en el estado LineBreak gate, pero no impide el merge por sí
solo (se apoya en revisores obligatorios).
Azure DevOps (Azure Repos + Azure Pipelines)
# azure-pipelines.yml
trigger:
branches:
include:
- main
pr:
branches:
include:
- "*"
pool:
vmImage: ubuntu-latest
# Container job alternative (image built from packages/gate/Dockerfile.ci):
# container: <registry>/linebreak-gate-ci:1
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: "3.11"
displayName: Python 3.11
- script: |
set -euo pipefail
mkdir -p "$HOME/bin"
# osv-scanner drives the dependency scan; without it the gate fails closed.
curl -fsSL -o "$HOME/bin/osv-scanner" https://github.com/google/osv-scanner/releases/latest/download/osv-scanner_linux_amd64
chmod +x "$HOME/bin/osv-scanner"
echo "##vso[task.setvariable variable=LINEBREAK_OSV_SCANNER_BIN]$HOME/bin/osv-scanner"
pip install --quiet "linebreak-gate>=1.13.4,<2"
displayName: Install linebreak-gate
# Scan + acceptance criteria, PR comment thread and PR status, exit 0/1/2.
# Secret variables are NOT exported automatically: map them here. An
# undefined $(NAME) stays literal; the gate treats such values as unset.
- script: linebreak-gate ci
displayName: LineBreak gate
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
LINEBREAK_LICENSE_KEY: $(LINEBREAK_LICENSE_KEY)
ANTHROPIC_API_KEY: $(ANTHROPIC_API_KEY)
- task: PublishBuildArtifacts@1
condition: always()
inputs:
pathToPublish: .linebreak/ci-out
artifactName: linebreak-gate-report
displayName: Publish the gate reportCuatro pasos manuales, en este orden:
Crear el pipeline desde
azure-pipelines.yml(Pipelines > New pipeline > Azure Repos Git > Existing YAML) y agregar las variables secretasLINEBREAK_LICENSE_KEYyANTHROPIC_API_KEY(Edit > Variables). Una variable que no existe queda como el texto literal$(NOMBRE); la compuerta la trata como no definida.Project settings > Repos > Repositories > el repositorio > Security: dar a la identidad
<proyecto> Build Service (<organización>)el permiso Contribute to pull requests. Sin eso, el comentario y el estado fallan con 403 (y el pipeline sigue bloqueando por código de salida).Repos > Branches >
main> Branch policies > Build validation: agregar este pipeline como Required, disparo Automatic. Esa política es la que deja el botón Complete apagado mientras el build esté rojo.Opcional: en la misma página, Status checks: exigir el estado
linebreak/gateque la compuerta publica en cada pull request.
En Azure Repos el disparador pr: del YAML no aplica: la política de Build
validation es la que corre el pipeline en cada pull request. pr: queda para
repositorios alojados en GitHub o Bitbucket y construidos desde Azure
Pipelines (en ese caso el comentario debe publicarse en ese proveedor; la API
de PR de Azure DevOps no aplica y la compuerta lo dice).
Imagen Docker: dos caminos
Imagen base de Python +
pip install(las plantillas de arriba). Funciona hoy sin publicar nada; descargaosv-scannerdesde GitHub en cada corrida (si el runner no tiene salida a internet, usar el camino 2).Imagen de CI de la compuerta, construida desde
Dockerfile.cien este directorio y publicada en un registro que el workspace o la organización pueda leer:docker build -f Dockerfile.ci -t <registro>/linebreak-gate-ci:1 . docker push <registro>/linebreak-gate-ci:1En Bitbucket:
image: <registro>/linebreak-gate-ci:1y se quitan las líneas decurlypip installdel script. En Azure: un container job (container: <registro>/linebreak-gate-ci:1bajopool) y se quita el paso de instalación. La imagen traeosv-scanner,git,bashycurl, corre como root y no defineENTRYPOINT: los tres son requisitos de Bitbucket Pipelines y de los container jobs de Azure.El
Dockerfilesin sufijo es la imagen del servidor MCP (entrypointlinebreak-gate mcp, usuario sin privilegios, sinosv-scanner) y no sirve para CI.
El comando
linebreak-gate ci [--path .] [--fail-on critical|high|medium|low]
[--story all|auto|<id>] [--manual auto|warn|block] [--stage auto|release|pr]
[--out-dir .linebreak/ci-out] [--no-comment] [--no-status]--story auto toma la historia del nombre de rama feat/<id> o story/<id>
(con sufijo permitido) cuando es una historia aprobada, y si no evalúa solo
las historias iniciadas. --manual auto es warn en un pull request y
block en cualquier otra corrida; --stage auto es pr en un pull request y
release en el resto. Los proveedores detectados son GitHub Actions, GitLab
CI, Bitbucket Pipelines y Azure Pipelines; en GitHub el comentario lo sigue
publicando la Action.
The spec loop — author, approve, serve over MCP, enforce
The gate also enforces approved acceptance criteria, and the whole loop is tool-agnostic — no LineBreak account, no desktop app, no server:
linebreak-gate spec new # scaffold a draft — fill it with any tool (your
# editor, Claude Code, ChatGPT), or distill it
# from the PRD you already have in Notion/Jira
linebreak-gate spec approve .linebreak/spec-draft.yml \
--approver "Ana Lopez <ana@example.com>" # a human on the record; commits
linebreak-gate mcp install --editor claude-code # or: cursor · codexlinebreak-gate mcp serves the approved bundle (.linebreak/spec/) over
MCP (stdio) to Claude Code, Cursor, Codex, or any MCP client. Six tools:
list_stories, get_story (criteria as agent context BEFORE code is
written), next_story, set_story_status, check_story (the same
evaluation engine CI runs, scoped to one story), and spec_status (approval +
offline signature state). Git is the transport — no network, no account,
works on a bare clone — and nothing in the bridge can write, edit, or
invalidate an approved criterion: criteria change only by editing the draft
and re-approving, with a human on the record.
spec approve prints what it is about to approve (and every statement that
changed, before and after) and refuses a draft that is not the one on the
remote; --local approves the copy on disk knowingly.
Then linebreak-gate check enforces the same criteria in CI: machine checks
run for real, manual criteria block until a recorded sign-off. A tests
criterion that executed zero tests fails, and a check can declare what it
expects to observe (expect: {output, exit}); the section "Integrity" of
docs/CRITERIA_ENFORCEMENT.md in the repository has the details. Guided first
run with the why of every step:
linebreakapp.com/en/start.
CLI
linebreak-gate init [--path .] [--fail-on critical|high|medium|low] [--force] [--non-interactive] [--provider auto|github|bitbucket|azure|all]
linebreak-gate ci [--path .] [--fail-on ...] [--story all|auto|<id>] [--manual auto|warn|block] [--stage auto|release|pr] [--out-dir DIR] [--no-comment] [--no-status]
linebreak-gate scan [--path .] [--fail-on critical|high|medium|low] [--format summary|json]
linebreak-gate report [--path .] [--format summary|json]
linebreak-gate override --finding <id> --reason "…" --approver <name/email> [--expires YYYY-MM-DD|--days N] [--path .]
linebreak-gate override --criterion <id> --reason "…" --approver <name/email> [--expires YYYY-MM-DD|--days N] [--path .]
linebreak-gate check [--path .] [--format summary|json] [--story <id> ...|--started-only] [--manual block|warn] [--stage release|pr]
linebreak-gate signoff --criterion <id> --approver <name/email> --note "…" [--path .]
linebreak-gate tickets sync [--path .] # retry pending ticket-tracker operations
linebreak-gate spec new [--path .] [--out <file>] [--force]
linebreak-gate spec approve <draft> --approver <name/email> [--role architect] [--path .]
[--against <remote>/<branch>] [--local]
linebreak-gate spec list|next [--path .]
linebreak-gate spec show|check <story-id> [--path .]
linebreak-gate mcp [--path .] # serve the approved spec over stdio
linebreak-gate mcp install [--editor claude-code|cursor|codex] [--print]
linebreak-gate badge [--format markdown|html|url]
linebreak-gate publish --to <governance-url> [--project <id>] [--path .] [--stage pr|release] [--run-id <id>] [--dry-run]initsets a repo up in one command: writes the pipeline file for the repo's CI provider (GitHub Actions workflow,bitbucket-pipelines.ymlorazure-pipelines.yml, detected from the git remote or chosen with--provider; never clobbers an existing one without--force), optionally writes.linebreak/gate.yml, offers to store the secrets via the GitHub CLI and to require thegatecheck — and prints the exact settings links for anything it can't do for you.ciis the whole run for CI providers without a native Action (Bitbucket Pipelines, Azure DevOps): scan, scoped check, evidence directory, PR comment and build status through the provider's API, exit with the worse code. See the Bitbucket / Azure section above.scanruns both detectors, writes git-native audit artifacts under.linebreak/audit/, and exits 0/1/2.reportrenders the recorded scan: counts by severity and every finding with CVE id, CVSS, advisory link, and override status.--format jsonfor machines.overriderecords a human-approved acknowledgment of one exact finding — the package + installed version + CVE tuple. A different CVE, a bumped version, or a new finding still blocks.--reasonand--approverare required; the record lands in the artifact's approval trail. Commit the updated.linebreak/audit/*.jsonso CI sees it.--expires YYYY-MM-DD(or--days N) bounds the acceptance in time: past that date the finding blocks again asexpired_riskuntil the acceptance is renewed or the finding is fixed (see Riesgos aceptados y tickets).checkevaluates the approved acceptance criteria (.linebreak/spec/, landed byspec approve) against the working tree:build/tests/commandrun for real,manualrequires a recorded sign-off. Exit 0 all satisfied (or no bundle — a clean no-op), 1 blocking (fail or needs-signoff), 2 tool/config/bundle error (fail closed). Writes.linebreak/audit/criteria.json. Scope flags (see Check scope):--story <id>(repeatable) evaluates only those stories,--started-onlyevaluates only stories with a started local state,--manual warnreports missing sign-offs without blocking,--stage prskips criteria markedcheck.when: release(listed as release-only, not evaluated; the default--stage releaseevaluates them). The summary and the JSON state the scope.signoffrecords an attributed human sign-off for onemanualcriterion under.linebreak/spec/signoffs/(additive;--approverand--noterequired). It binds to the criterion as approved — editing the criterion and re-approving the spec makes prior sign-offs stale. Commit the record.override --criterionrecords a human-approved override for one failed machine criterion in.linebreak/audit/criteria.json— same philosophy as CVE overrides: possible, always attributed, stale once the criterion is edited. Other blocking criteria still block.spec new/spec approve— the tool-agnostic authoring path (see the spec-loop section above): scaffold a draft, fill it with any tool, land it as the approved bundle with an attributed human approval, committed. Unsigned local approvals are markedidentity_source: client; cryptographic signatures come from the governance service (license key).spec listprints the approved acceptance criteria bundle: each story, its criteria with check types, and the approver attribution. Read-only. Exit 0 on a valid bundle or when none exists; exit 2 on a malformed bundle (fail closed on structure).spec next/show/checkare the CLI twins of the MCP bridge tools.
Publish to the panel
linebreak-gate publish --to https://governance.example --project <id> sends
the recorded run (criteria, findings, sign-offs, overrides, attestation) to a
LineBreak governance service so executives and auditors see it in the panel.
The bearer comes from LINEBREAK_GOV_TOKEN (a token issued by that service),
else LINEBREAK_GOVERNANCE_TOKEN; --to defaults to
LINEBREAK_GOVERNANCE_BASE_URL and --project to LINEBREAK_GOV_PROJECT. Run
it after scan and check, as the last step of the job.
Publishing never blocks a change: a missing token, an unreachable service,
or a rejected body prints a warning and exits 0. The verdict was already given
by scan/check; publish only reports it. Under GitHub Actions the run_id
is derived from the run id and attempt, so re-running the step is idempotent
server-side. --dry-run prints the body without sending.
Governance credentials: one lookup
Every command that talks to the governance service (check for the panel's
sign-offs and the tracker configuration, signoff / override for the
verified identity, publish, report --from-governance) finds the service
URL, token and project the same way, the first that applies:
environment variables (
LINEBREAK_GOVERNANCE_BASE_URL,LINEBREAK_GOVERNANCE_TOKEN,LINEBREAK_GOV_TOKEN,LINEBREAK_GOV_PROJECT); a set variable always wins;~/.config/linebreak/governance.env($XDG_CONFIG_HOME/linebreak/when set);~/.config/linebreak/governance-local.env(a local instance);~/.linebreak/env, kept for compatibility with the desktop app.
Files are KEY=value lines (export and quotes allowed). Only the first file
that carries a token is used, whole: a URL from one file is never paired with a
token from another. LINEBREAK_GOV_CREDENTIALS=off turns the files off. In CI
there are no such files, so pipelines behave as before; on a workstation, a
token in one of them makes signoff and override record the verified
governance identity (and refuse if that token fails), exactly as the exported
variable does.
Read the published run: report --from-governance
The scan runs in CI; report alone only reads the scan recorded on this
machine. linebreak-gate report --from-governance --project <id> reads the
last run CI published instead (GET /v1/reports/projects/{id} for the list,
GET /v1/projects/{id}/gate-runs/{run_id} for the run): findings by severity
with their acceptances and KEV marks, the criteria counts and the verdict as
that run published it. --stage pr|release takes the last run of that stage,
--run-id a specific one, --format json the whole run. Read only; exit 2
when the service cannot be read, 0 otherwise (also when nothing was published
yet).
Badge
Show visitors the repo is gated. linebreak-gate badge prints a ready-to-paste
README snippet (no network calls — the shields.io static badge is fully encoded
in its URL); --format html|url for the tag or bare-URL variants:
[](https://www.linebreakapp.com/en/gate)Check scope: per story on PRs, full at release
A team that approves the whole sprint up front (the flow this gate promotes: spec approved before code) would otherwise see every PR blocked by criteria of stories nobody has started. The fix is scope, not a weaker gate:
Scan always. The dependency and code scans run on every PR and on release, unchanged.
Check the story on PRs.
linebreak-gate check --story <id>evaluates only that story's criteria (--storyrepeats);--story autotakes the story from afeat/<id>orstory/<id>branch, else started stories only.--started-onlyevaluates only stories whose local state isdoing,review, ordone(the statespec nextand the MCP bridge write); stories without a state are listed as not started and do not count. When no story is started at all (no state file, an unreadable one, or an external tracker without local states) the scope selects nothing and the check is exit 2, never a vacuous pass.--manual warnreportsmanualcriteria without a sign-off as pending instead of blocking, so a sign-off that belongs to the release does not hold a PR.Check everything at release. The release job runs the full bundle with
--manual block(the default): every criterion of every story, everymanualcriterion signed off. Every run writes.linebreak/audit/criteria.jsonstamped with itsscopeandpending_signoffs, so a scoped or relaxed run is evidence of that run and can never be read as a full verdict (and CI never uploads a stale one).
The summary prints a scope: line (mode, stories evaluated, criteria counted,
manual policy, stage), a release-only (not evaluated at stage pr): line when
criteria were skipped, and one pending sign-off: line per missing sign-off;
the JSON carries the same under scope (including stage and
release_only) and pending_signoffs. An unknown --story id
is exit 2 (a scope that names nothing is a mistake, never a pass).
In the GitHub Action the same pattern is three inputs (stage is described below). story is all (every
story, the default), auto (infer the id from a feat/<id> or story/<id>
branch, a trailing slug allowed as in feat/<id>-add-login, when it is an
approved story; otherwise started stories only), or an explicit id. manual
is warn or block; left empty it is warn on pull_request and block on
every other event. The PR comment shows the resolved scope, the stories not
started, and the pending sign-offs.
Behavior change for existing @v1 users (1.11.0): the manual default on
pull_request events is now warn, so a manual criterion without a
sign-off no longer blocks a PR unless the workflow sets manual: block. Add
the release job below (or set manual: block on the PR job) to keep
sign-offs enforced.
# .github/workflows/security-gate.yml, PR gate + release gate
name: Security gate
on:
pull_request:
push:
tags: ["v*"] # the release job runs on release tags
permissions:
contents: read
pull-requests: write
jobs:
gate:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: Baktun-Studio/linebreak-gate@v1
with:
license-key: ${{ secrets.LINEBREAK_LICENSE_KEY }}
story: auto # this PR's story, or started stories only
manual: warn # sign-offs are listed, not blocking, on PRs
stage: pr # check.when: release criteria are listed, not evaluated
release-gate:
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: Baktun-Studio/linebreak-gate@v1
with:
license-key: ${{ secrets.LINEBREAK_LICENSE_KEY }}
story: all # every story, every criterion
manual: block # every manual criterion needs its sign-off
stage: release # the default; release-only criteria run and blockRequire the gate check on the default branch and the release-gate job
before publishing. Generic CI: the same two invocations of the CLI, with the
exit codes respected.
Release-only criteria: check.when: release
A criterion checked by a script against a shared staging environment fails
for every PR the moment staging regresses, including the PR that fixes it.
Mark it when: release in the spec:
- id: checkout-smoke
statement: The checkout smoke script passes against staging.
check:
type: command
payload: ./scripts/smoke-staging.sh
when: release # absent means alwaysWith stage: pr on the PR job (check --stage pr) such criteria are not
evaluated: their checks never run, the report lists them as [release-only]
with their own count, and they are neither pass nor fail. The release job
(stage: release, the default) evaluates them as always, and a failing one
still blocks the release. The field is part of the criterion's content, so
adding it to an approved criterion re-arms that criterion's sign-offs and
overrides like any other edit.
Release in two phases: check.when: attestation and --phase
A criterion like "the release carries a signed attestation" is signed ON a
green release run, so the run that produces it cannot require it. Mark that
manual criterion when: attestation:
- id: release-attestation
statement: The release carries a signed attestation of its green run.
check:
type: manual
when: attestationThen release in two runs: check --phase prepare (Action input phase: prepare) evaluates everything except that criterion, which shows as
[awaiting-attestation]; the approver signs it off against that run; check
(--phase verify, the default) enforces it like any manual criterion, and
that run is the release. Other manual criteria block in both phases.
Shared environments: check.resource and check.environment
- id: f1-e2e
statement: Flow F1 end to end against staging.
check:
type: command
payload: node scripts/qa/flows.mjs --base "$STAGING_URL" --flow f1
when: release
resource: staging-demo-merchant # runs alone; a clash is reported as such
environment:
name: staging
version_url: ${STAGING_URL}/version # answers the deployed commitChecks naming the same resource never overlap on one machine (an OS lock),
and a failed one is re-run once alone: passing alone is [collision] (not a
defect, not blocking), failing again is a failure "reproduced when re-run
alone". Across machines, serialize the jobs in CI or give every end-to-end run
its own disposable tenant. environment records the deployed version the
check measured and warns (never blocks) when it is not the evaluated commit:
behind, ahead, diverged, unreachable, or changed while the check ran.
Configuration — .linebreak/gate.yml
The gate's strictness is governance, so it lives in the repo — changing the threshold is itself a PR: visible, reviewable, attributable in git history.
# .linebreak/gate.yml
fail_on: critical # critical (default) | high | medium | low
exclude_paths: # optional: root-relative globs excluded from scanning
- fixtures
- "sandbox/*"
code_scan: auto # auto (run when model credentials are set) | on (required) | off
security: # exploitation policy, see "Prioridad por explotación" below
block_kev: true # default: anything in CISA's KEV catalog blocks
epss_threshold: 0.5 # default: off
criteria:
enforce: true # default: true whenever a spec bundle exists; false disables
# criteria checking only (the security scan is unaffected)
integrity: warn # warn (default) | block | off: shared tests between stories,
# statements naming identifiers gone from the code, commands that ran 0 tests
risk_acceptance: # optional: accepted risks expire (see the section below)
max_days: 90 # longest acceptance allowed
required: true # an override without --expires/--days is refused
tickets: # optional: mirror every acceptance into your tracker
provider: jira # jira | github
project: SEC # Jira project key, or "owner/repo" for GitHub Issues
labels: [linebreak]Precedence: explicit --fail-on flag / Action input → .linebreak/gate.yml →
built-in default (critical). fail_on may live at the top level or under
security: (not both). An invalid config is a tool error (exit 2):
a broken governance file never silently falls back to a default.
Prioridad por explotación
Un CVSS alto dice cuánto daño haría una vulnerabilidad; no dice si alguien la está usando. Desde 1.13.2 la compuerta enriquece cada hallazgo de dependencias con dos fuentes públicas y ordena y bloquea por explotación real:
KEV (catálogo de vulnerabilidades explotadas conocidas de CISA): "la están explotando hoy".
EPSS (FIRST): probabilidad de explotación en los próximos 30 días, entre 0 y 1: "probable en 30 días".
Política
# .linebreak/gate.yml
security:
fail_on: high # piso de severidad (o en la raíz del archivo, no en ambos)
block_kev: true # cualquier hallazgo en KEV bloquea, sea cual sea su severidad (por defecto)
epss_threshold: 0.5 # bloquea desde esta probabilidad a 30 días (apagado por defecto)
intel_max_age_hours: 24 # antigüedad máxima de la caché (por defecto 24 h)
intel: true # false apaga el enriquecimiento por completo (red y caché)Un hallazgo bloquea por cualquiera de tres disparadores: severidad en o sobre
el piso, presencia en KEV (con block_kev) o EPSS en o sobre el umbral. El
motivo queda registrado en cada hallazgo (block_reason) y agregado en la
corrida (block_reasons) con exactamente dos valores, que el servicio de
gobierno lee por separado: kev (en el catálogo) y vulnerability (piso de
severidad o umbral EPSS). Cuando aplican los dos, gana kev. Un override
registrado con linebreak-gate override --finding <id> reconoce el hallazgo
exacto igual que siempre, cualquiera sea el motivo.
Orden y puntaje
El report, el veredicto y el JSON listan los hallazgos en este orden:
primero los que están en KEV, luego por EPSS de mayor a menor, luego por
severidad y CVSS. Un hallazgo sin puntaje EPSS (sin CVE, o un CVE que FIRST
aún no puntúa) va después de los puntuados, ordenado por severidad. Cada uno
muestra su etiqueta: explotada activamente (KEV), EPSS 0.93 o sin datos de explotación.
[BLOCKING: kev] CVE-2021-44228 critical cvss 10.0 log4j-core@2.14.1
explotada activamente (KEV), EPSS 1.00, CISA due 2021-12-24 risk 100
[BLOCKING: vulnerability] CVE-2024-3094 critical cvss 10.0 xz@5.6.0
EPSS 0.86 risk 100
[below floor] CVE-2019-0001 low cvss 2.0 x@1
EPSS 0.03 risk 20El risk_score sube con la explotación y nunca baja: un hallazgo en KEV vale
100 sin importar su severidad; el EPSS eleva el puntaje hasta su probabilidad
en porcentaje (un medium con EPSS 0.93 vale 93); la severidad es el piso
(critical 100, high 80, medium 50, low 20). Sin datos de explotación los
números son los de siempre.
Caché y modo sin red
Las dos fuentes se guardan en .linebreak/cache/ (la carpeta se ignora sola
en git; el registro de auditoría es .linebreak/audit/security.json, no la
caché). Con caché fresca no hay ninguna llamada de red. Con caché vencida se
consulta la red y, si falla, se usa la caché vencida y el reporte lo dice
(stale). Sin red y sin caché los campos epss y kev quedan vacíos
(null) y el reporte dice sin datos de explotación: el enriquecimiento
nunca convierte un escaneo en error ni cambia el veredicto que habría dado
sin datos. LINEBREAK_OFFLINE=1 salta la red (la caché sigue usándose), útil
en pipelines sin salida a internet; security.intel: false apaga todo.
En el artefacto, kev: true es "está en el catálogo", kev: false es "se
consultó el catálogo y no está" y kev: null es "no se pudo consultar" (o el
hallazgo no tiene CVE): para un auditor son tres hechos distintos. La corrida
registra además exploit_intel (fuente y versión del catálogo de esa
corrida) y verdict con sus block_reasons.
Fuentes: https://api.first.org/data/v1/epss (por lotes de CVE) y
https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json.
Ninguna requiere credenciales.
Roles e identidad de quien firma (.linebreak/roles.yml)
Sin este archivo, cualquiera puede correr signoff u override y el nombre
que queda en el registro es el que la persona escribió (identity_source: client). Con él, la compuerta sabe quién puede firmar qué:
# .linebreak/roles.yml
roles:
ciso:
members: [ana@example.com]
can:
sign_criteria: ["*"] # patrones sobre el id del criterio, la historia o la épica
approve_overrides: ["*"]
accept_security_risk: [critical, high, medium, low]
qa:
members: [luis@example.com, "github:luis-qa"]
can:
sign_criteria: ["e12-*"]
approve_overrides: []
accept_security_risk: [low, medium]
policy:
require_roles: true # una firma sin rol autorizado se rechaza
require_verified_identity: false # true: una identidad solo declarada no cuentasignoffyoverrideregistran el rol con el que se firma:--role, o se infiere cuando la persona tiene exactamente un rol que lo permite. Si no tiene ninguno yrequire_rolesestá activo, el comando falla y dice qué rol haría falta. Aceptar un hallazgo de seguridad se limita por severidad.check,scanyreportvuelven a evaluar cada firma y cada override contra los roles vigentes. Un registro cuyo rol ya no existe, cuyo miembro salió, o que no cubre ese criterio, se rechaza con motivorole_deniedy una línea legible (role denied: <id> (<historia>): ...); bloquea aunque el check corra con--manual warn. El registro no se borra: queda como evidencia y una firma posterior autorizada lo reemplaza.Identidad: además de
client, la compuerta reconocevcs(en CI toma el actor del proveedor:GITHUB_ACTOR,GITLAB_USER_EMAIL,BITBUCKET_STEP_TRIGGERER_UUID,BUILD_REQUESTEDFOREMAIL) ygovernance(conLINEBREAK_GOVERNANCE_BASE_URLyLINEBREAK_GOVERNANCE_TOKENconsultaGET /v1/mey usa esa identidad; un token configurado que falla detiene el comando, nunca cae en silencio a un nombre escrito). La identidad verificada manda; lo que se escribió en--approverse guarda comodeclared_approver. En la lista de miembros se puede poner un correo o la formaproveedor:login(github:luis-qa).policy.require_verified_identity: truehace que una firma conidentity_source: clientse registre como declarada pero no cuente: el comando lo avisa al firmar ycheckla rechaza con motivoidentity_unverified.Sin archivo, o con ambas políticas en
false, nada cambia: nada se rechaza y los registros llevanrole: null. Un archivo malformado es error de configuración (exit 2), nunca una omisión silenciosa.
Audit records
Every scan and every override is recorded in .linebreak/audit/security.json
(dependencies) and .linebreak/audit/code.json (AI SAST) — the same versioned
document format the LineBreak tools write, carrying findings (CVE id,
CVSS, advisory link), scanner engine, timestamp, actor, and the approval trail
with each override's reason + approver, the role it was made under, and the
identity source (client, vcs, governance). Who relaxed the gate, and
when, is itself auditable.
Riesgos aceptados y tickets
Una excepción registrada con override era para siempre: la persona que
aceptó el riesgo se va y el riesgo se queda. Desde la versión 1.13.3 cada
aceptación puede (o debe) vencer, y cada excepción vive también en el gestor de
tickets del equipo, para que la trazabilidad quede en su herramienta y no solo
en LineBreak.
Vencimiento
linebreak-gate override --finding <id> --reason "…" --approver ana@example.com --expires 2026-12-31
linebreak-gate override --criterion <id> --reason "…" --approver ana@example.com --days 30--expires YYYY-MM-DDo--days Nfijan la fecha en que la aceptación deja de valer. Vale hasta ese día inclusive; al día siguiente el hallazgo (o el criterio) vuelve a bloquear con motivoexpired_risk. El informe dice qué hallazgo es, quién lo aceptó, cuándo venció y las dos salidas: renovar con otrooverrideo corregir.Con menos de 14 días por vencer,
scan,reportycheckavisan sin bloquear (expiring risk/expiring exception).La política vive en
.linebreak/gate.yml:risk_acceptance: max_days: 90 # ninguna aceptación puede ir más allá de 90 días required: true # un override sin vencimiento se rechaza (exit 2)Sin este bloque, las aceptaciones sin fecha siguen permitidas (el comportamiento anterior). La misma política aplica a hallazgos de seguridad y a excepciones de criterios.
Renovar es registrar un
overridenuevo sobre el mismo objetivo. La aceptación anterior no se pisa: queda en el historial del artefacto (.linebreak/audit/security.json,code.json,criteria.json) y la más reciente es la que rige. Cada entrada guardaexpires, yticketcuando hay gestor configurado.En
--format json,scan/reporttraenblock_reasons(vulnerability,expired_risk) y, por detector,expiredyexpiring;checktraeblock_reasons,expired_overridesyexpiring_overrides.
Tickets (Jira primero, GitHub Issues también)
# .linebreak/gate.yml
tickets:
provider: jira # jira | github
project: SEC # clave del proyecto en Jira, o "owner/repo" para GitHub Issues
labels: [linebreak] # etiquetas que llevan todos los tickets
main_branch: main # rama cuyos scans abren tickets de atención (opcional)
issue_type: Task # solo Jira: tipo de incidencia a crear (opcional)Credenciales por entorno, nunca en el repositorio: JIRA_BASE_URL,
JIRA_EMAIL y JIRA_API_TOKEN para Jira (API REST v2, funciona en Cloud y en
Data Center); GITHUB_TOKEN para GitHub Issues.
Qué hace la compuerta cuando hay un tickets: configurado:
Momento | Qué pasa en el gestor |
| Crea un ticket con el hallazgo o criterio, quién aceptó, motivo, vencimiento, repositorio, commit y enlace al registro de evidencia. La clave queda en la entrada de evidencia ( |
| Comenta el ticket y lo reabre si estaba cerrado. Un solo comentario por fecha de vencimiento, aunque el gate corra en cada push. |
El hallazgo desaparece del scan, o el criterio pasa por sí solo en un | Comenta y cierra el ticket. |
Un | Abre un ticket de atención (vulnerabilidad nueva sobre código ya liberado). |
Detalles que conviene saber:
El gestor nunca bloquea el veredicto. Sin red, credenciales malas o un proyecto que rechaza la incidencia: el veredicto es el que dicen los artefactos, se imprime un aviso, el error queda en la evidencia (
ticket_erroren la entrada) y la operación queda pendiente en.linebreak/audit/tickets.json.linebreak-gate tickets syncreintenta lo pendiente (exit 0 cuando no queda nada; exit 1 si algo sigue pendiente).La idempotencia vive en el gestor. Cada ticket lleva las etiquetas
linebreak-target-<hash>ylinebreak-artifact-<security|code|criteria>; antes de crear, la compuerta busca por etiqueta. Así un runner de CI que no tiene el libro local nunca duplica tickets, y puede cerrar los de hallazgos corregidos."Nuevo" en la rama principal se mide contra el artefacto anterior en
.linebreak/audit/(el que estaba comprometido antes de reescribirlo). El primer scan de un repositorio no abre nada: no hay línea base. Solo cuentan hallazgos a la altura del umbralfail_ono por encima, y que no tengan ya un ticket. En GitHub Actions la rama se lee deGITHUB_REF_NAME; un pull request (GITHUB_HEAD_REF) nunca cuenta como rama principal.Conviene comprometer
.linebreak/audit/tickets.jsonjunto con eloverride(igual quesecurity.json), para que el historial del ticket viaje con la evidencia.
Pricing
Free, forever: the dependency CVE scan and the whole spec loop — authoring, human approval, MCP serving, and CI enforcement. No key, no account.
Pro — $99/month per team (pricing):
cryptographically signed, tamper-evident approvals (Ed25519, verifiable
offline), required-key enforcement mode, and hosted AI code review with no
API key to manage. Buy on the pricing page — your LINEBREAK_LICENSE_KEY
arrives by email within seconds (it's the Action's license-key input).
Prefer your own model key? ANTHROPIC_API_KEY also enables the AI review;
Pro's hosted review is the zero-config path.
The gate runs open by default: it works without a key and prints a notice
when no LINEBREAK_LICENSE_KEY is set (suppressed for BYOK users). That's
freemium — the dependency scan runs free. Teams that want to require a valid
Pro key for the gate to run at all can opt into
LINEBREAK_ENTITLEMENTS_PROVIDER=remote, which checks the entitlement before
any scan and fails closed on a missing/invalid/revoked key, wrong plan, or
unreachable service — blocking the whole gate, dependency scan included.
Available Tools
6 toolscheck_storyA
Run this story's acceptance criteria against the working tree with
the SAME engine as the merge gate (linebreak-gate check). Returns
pass | fail | needs-signoff per criterion — verify your work here
BEFORE pushing instead of discovering failures at the merge.
| Name | Required | Description | Default |
|---|---|---|---|
| story_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description must disclose behavior. It mentions the engine equivalence and return values (pass|fail|needs-signoff), but does not explicitly state whether it modifies anything or any prerequisites, leaving some behavioral ambiguity.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences, front-loaded with the core purpose and engine, and contains no filler words. Every phrase adds value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With a single parameter, no annotations, and an output schema present, the description adequately covers purpose, return format, and usage timing. It lacks minor details like prerequisites or edge cases, but remains complete for a simple check tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage for the only parameter story_id is 0%, and the description only refers to 'this story' without explaining the ID format or how to obtain it. Since the schema lacks descriptions, the description should compensate but doesn't.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool runs a story's acceptance criteria using the same engine as the merge gate, specifying the verb 'Run' and the resource 'acceptance criteria'. It distinguishes itself from sibling tools like get_story or spec_status by focusing on the local check before pushing.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says to use it before pushing to avoid discovering failures at the merge, providing clear temporal context. It doesn't name alternatives but the instruction is unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_storyA
One approved story in full: title, epic, and every acceptance criterion with its id, statement, and check type (build | tests | command | manual). Read this BEFORE implementing the story — the criteria are the approved definition of done.
| Name | Required | Description | Default |
|---|---|---|---|
| story_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the burden. It discloses the returned fields and the key behavioral fact that the criteria are the approved definition of done. However, it does not explicitly state read-only behavior, potential errors, or authorization requirements, though 'Read this' implies a non-mutating operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences with front-loaded output summary and a clear usage directive. Every sentence adds value, with no filler or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the key output fields and usage timing, and an output schema exists for return details. It stops short of explaining error/not-found behavior or explicitly comparing to sibling tools, but for a simple single-id retrieval tool it is sufficiently complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has one required story_id with 0% schema description coverage. The description does not elaborate on the parameter, leaving the agent to infer that story_id identifies the story. This adds no meaning beyond the parameter name, so it does not compensate for the lack of schema documentation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool returns 'One approved story in full' with title, epic, and every acceptance criterion. This specifies a verb (retrieve/get), resource (story), and the detailed scope, distinguishing it from sibling tools like list_stories which would provide a listing.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs to read this tool 'BEFORE implementing the story,' establishing the intended usage context. It does not name alternatives or exclusions, but the emphasis on 'approved definition of done' implies this is the authoritative source for implementation.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_storiesA
All approved stories in this repository: id, title, epic, local status (todo|doing|review|done), and how many acceptance criteria each carries. The list IS the approved scope — nothing else is in spec.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It discloses output fields, includes the local status enum, and reveals the key behavioral trait that this list is the sole approved scope. It does not explicitly state read-only behavior or ordering, but the scope is well communicated.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, with the most important information front-loaded ('All approved stories'), and no filler. Every word contributes to understanding the tool's output and scope.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter list tool with an existing output schema, the description fully covers purpose, return fields, status semantics, and the concept of approved scope, making it complete without needing to explain return values or parameters.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There are zero parameters, and schema coverage is 100%, so no parameter clarification is needed. The baseline for no-parameter tools is 4 and the description adds no unnecessary parameter noise.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('list') and resource ('approved stories in this repository'), enumerates the exact fields and status values returned, and clarifies that it represents the approved scope, clearly distinguishing it from sibling tools like get_story or spec_status.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'The list IS the approved scope' provides clear context for when this tool is appropriate, but it does not explicitly name alternatives or state when not to use it, falling just short of full exclusion guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
next_storyA
The next approved story that is not yet done, per local story state. Use this to pick up work without guessing at priorities.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of disclosing behavioral traits. It mentions the dependency on 'local story state' and the selection logic, but it does not explicitly state whether the operation is read-only or has side effects (e.g., marking the story as in progress). This ambiguity is a notable gap for an agent deciding whether to call this tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences with no redundancy. The first sentence defines the output and selection criteria; the second provides usage guidance. Every word earns its place, and the structure is front-loaded with the core purpose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the simplicity (no parameters) and presence of an output schema, the description covers the essential aspects: what is returned (next approved, not done), the dependency (local state), and the use case (picking up work). It lacks explicit edge-case behavior (e.g., behavior when no approved story exists) but this is a minor omission for a tool of this complexity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so the schema is empty. The description correctly focuses on behavior rather than parameters. According to the calibration, a zero-parameter tool gets a baseline of 4, and the description does not need to add parameter information.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies the tool's function: returning 'the next approved story that is not yet done, per local story state.' This specific selection criteria (approved and not done) distinguishes it from siblings like list_stories (all stories) and get_story (a specific story). The verb 'pick up work' implies retrieval of a single next item.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use the tool: 'Use this to pick up work without guessing at priorities.' This provides a clear use case and implies this tool is preferable over manual priority guessing from list_stories. However, it does not explicitly name alternative tools or exclude when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
set_story_statusA
Record story progress: status is doing | review | done. Writes LOCAL story state only (the repo's tracker-sync artifact) — never a configured external tracker, and never the approved criteria.
| Name | Required | Description | Default |
|---|---|---|---|
| status | Yes | ||
| comment | No | ||
| story_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It transparently states that this tool writes only the local tracker-sync artifact and never external trackers or criteria. This reveals side-effect scope clearly, though it does not detail permissions, reversibility, or return behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences, front-loaded with the primary action, and every word adds value. It avoids redundancy and is appropriately sized for the tool's complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple setter with an output schema, the description adequately covers scope, status values, and side-effect limitations. It does not mention prerequisites (e.g., story must exist) or error cases, but these are not critical for basic usage given the output schema exists and sibling tools are available.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It adds crucial semantics by restricting status to 'doing | review | done', which is not in the schema as an enum. However, it does not explain the optional comment parameter or clarify the story_id format beyond its obvious meaning.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description begins with 'Record story progress', which is a specific verb+resource pairing. It also enumerates the valid status values (doing | review | done) and explicitly scopes the tool to local story state, distinguishing it from sibling tools like check_story or spec_status.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description clearly states when this tool is appropriate (recording local story progress) and provides explicit exclusions: 'never a configured external tracker, and never the approved criteria.' However, it does not name sibling alternatives explicitly, so it stops short of a full 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
spec_statusA
Is there an approved spec bundle: version, source phase, approver, story count, and the approval signature state (verified | invalid | signed-unverified | unsigned), verified entirely offline.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It discloses that verification is done entirely offline, which is useful contextual behavior. However, it does not explicitly state whether the operation is read-only or if it has side effects, though 'status' implies a safe query. More explicit disclosure of non-mutating behavior and authorization needs would improve transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single sentence that efficiently packs the purpose and the returned fields. It is front-loaded with the main query, but the list of fields makes it slightly dense. Still, every word contributes to the meaning, and there is no fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given that the tool has zero parameters and an output schema exists, the description provides enough context about the core purpose and included attributes. It does not mention any prerequisites or failure scenarios, but for a simple status query, the description is sufficiently complete within the existing structured context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has zero parameters, so there are no parameters to document. According to the rubric, zero parameters earns a baseline score of 4, and the description adds no irrelevant parameter information. No compensation is needed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly indicates this tool checks for and reports on an approved spec bundle, listing specific fields (version, source phase, approver, story count, approval signature state). It distinguishes itself from sibling tools by focusing on bundle approval status rather than individual stories. However, the question format ('Is there...?') is slightly less direct than a verb like 'Get' or 'List'.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage for verifying the state of an approved spec bundle, but it does not explicitly state when to use this tool versus siblings like check_story or list_stories. There are no exclusions or alternative tool references. The 'verified entirely offline' hint suggests a context but not comparative guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
6 tool updates
v0.1.0- First observed
check_story - First observed
get_story - First observed
list_stories - First observed
next_story - First observed
set_story_status - First observed
spec_status
TDQS
Scored across 6 tools
Each tool targets a distinct function: listing stories, picking the next one, retrieving full story details, updating status, checking acceptance criteria, and inspecting spec status. There is no overlap in purpose, making selection unambiguous.
Most tools follow a clear verb_noun pattern (list_stories, get_story, set_story_status, check_story), but 'next_story' and 'spec_status' deviate from this pattern. Still, all names are readable and consistent in style (lowercase underscores), with only minor deviations.
Six tools is well within the ideal range and each tool earns its place, covering the core lifecycle of story selection, status updates, and gate verification without redundancy.
The set covers the entire local workflow: enumerate stories, pick next, get full details, update status, run acceptance checks, and verify spec approval. No obvious dead ends or missing operations for its stated purpose.
Maintenance
Related MCP Connectors
Roadmap, tasks, releases and user feedback your coding agent reads and writes over MCP.
Control plane for autonomous software labor. Agents claim objectives over MCP with audit trail.
Team docs served to AI agents over MCP - search, Markdown reads, version pinning, read audit.
Agent-native notes, tasks, dev-docs, vaults, sync & handoffs. MCP + OpenAPI dual surface.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceAI-native project management with persistent memory for coding agents. 17 MCP tools for features, stories, sprints, architecture decisions, knowledge base, and session tracking.3MIT
- AlicenseAqualityBmaintenanceAn MCP server that gives AI agents structured read/write access to a story-based project backlog. Agents can list stories, read content, update status, and append notes — all backed by plain markdown files that live inside your project repository. There is no shared server. The backlog files live in your repo under requirements/, committed and versioned alongside your code163-
- FlicenseNot gradedqualityCmaintenanceEnables AI agents to manage local project files and Git operations through MCP tools, including file CRUD, search, Git status, recent commits, and project summaries.-
- AlicenseNot gradedqualityDmaintenanceMCP server that connects AI coding agents to Greenroom's per-story Storybook reviews, allowing them to list feedback with screenshots, reply to threads, mark stories addressed, and approve stories when explicitly delegated.MIT