Repository configuration
A root-level .secondshift.yaml declares what runs and when. The server
controls models, credentials, sandbox settings, and runtime policy.
A skill defines a task: it can analyze repository files, gather service data, or take actions through approved MCP tools and HTTP API operations. The review below is a simple first example. See action tasks for a workflow that changes connected service data.
SecondShift reads only committed files. Runs use the synchronized Git
revision. You can add a repository before it has a manifest. Its page shows
what is missing, and the first committed .secondshift.yaml syncs on its own.
A manifest that has never synced shows as a warning. One that breaks after a
good sync shows as an error, and runs keep the last good sync.
A local repository follows the branch that was checked out when it was added. SecondShift reads the manifest and skills from that branch’s newest commit, so edits in the folder and other branches never change what runs. The worker checks the branch every five minutes and syncs new commits on its own. A commit with a broken manifest is skipped, and runs keep the last good sync. Owners and admins can turn auto-sync off on the repository’s page and press Resync instead. Syncing grants access only through connections that do not require approval, and only for tools the owner allowed. Other requests wait for an owner.
Your first job
Section titled “Your first job”Begin with a manual run, check its results and tool activity, then enable a schedule if useful. A proposed schedule is a starting point. Verify the skill’s access and outcome before letting it run unattended.
Create .agents/skills/repository-review/SKILL.md:
# Repository review
Read the README and inspect the source for correctness problems and outdateddocumentation. Run the documented tests if their dependencies are available.Do not install packages, use the network, or modify files.
Report confirmed findings with file paths, line numbers, user impact, and asuggested fix. List the checks you ran and anything you could not verify.Add .secondshift.yaml:
version: 1
jobs: repository-review: name: Weekly repository review skill: .agents/skills/repository-review/SKILL.md schedule: "0 9 * * 1" timezone: Europe/WarsawCommit both files, add the repository in SecondShift, and connect a default model provider. Open the discovered job and choose Run now to try it. Enable the job when you want it to run on schedule.
For a ready-made skill that needs only a model, see the documentation drift example.
The sandbox has no network access or provider credentials. Dependencies must already be available in the sandbox image. Reading files and running standard-library tests work without installing packages.
Manifest fields
Section titled “Manifest fields”| Field | Required | Meaning |
|---|---|---|
version |
No | Manifest version. Only 1 is supported and is the default. |
jobs |
Yes | Mapping of job keys to job definitions. |
jobs.<key>.name |
Yes | Display name in SecondShift. |
jobs.<key>.skill |
Yes | Repository-relative path to a SKILL.md file. |
jobs.<key>.schedule |
No | Cron schedule, such as 0 9 * * 1 for Monday at 09:00. Leave it out for a job that runs only with Run now. A tick is skipped while the job has a run that has not finished. |
jobs.<key>.timezone |
No | IANA timezone, such as Europe/Warsaw. Defaults to UTC. |
jobs.<key>.history |
No | How many of the job’s own earlier reports each run starts with, up to the server’s SECONDSHIFT_JOB_HISTORY_MAX (5 by default). Defaults to 0. See run history. |
jobs.<key>.notify |
No | Webhook notification bindings requested by this job. |
jobs.<key>.triggers |
No | Events that can start the job. Only webhook is supported. An owner or admin turns it on. See webhook triggers. |
jobs.<key>.webhook |
No | Which webhook requests start a run: skip_when, repeat_key and repeat_window. Needs triggers: [webhook]. See ignore requests and repeats. |
jobs.<key>.open_pull_request |
No | Set to true to open a draft pull request with each run’s changes. An owner must allow pull requests for the repository. See draft pull requests. |
jobs.<key>.open_issues |
No | Set to true, or to labels and max, to let the agent propose GitHub issues. They open after each successful run. An owner must allow issues for the repository. See GitHub issues. |
jobs.<key>.requires |
No | MCP tools and HTTP API operations the job needs. See below. |
Skill paths must stay inside the repository. Unknown fields are rejected. Use separate job definitions for skills with different schedules or access requirements.
Run history
Section titled “Run history”A job that compares against its last run, such as a weekly review, can set
history. Each run then starts with the job’s last N successful reports, newest
first, after the task in its prompt:
jobs: weekly-review: name: Weekly review skill: .agents/skills/weekly-review/SKILL.md history: 1Only the same job’s reports are included: a job is its repository and key, so
renaming the key starts with no history. Failed and canceled runs are left out.
The reports are chosen when a run is created and are shown in its stored input.
Together they are capped by the server’s SECONDSHIFT_RUN_HISTORY_MAX_CHARS,
newest first, so an older report may be cut or left out. One removed by
retention is marked as no longer stored. Reports are marked as earlier output, not instructions,
because they may quote untrusted text. Reading its own reports gives a job no
new access, so history needs no approval.
Reports are kept as long as their runs, SECONDSHIFT_RUN_RETENTION_DAYS (90 by
default). A job that runs less often than that, such as a quarterly review,
finds its previous report already removed: raise the retention or leave
history off for that job.
Draft pull requests
Section titled “Draft pull requests”A job can open a draft pull request with the agent’s changes:
jobs: docs-drift: name: Docs drift skill: .agents/skills/docs-drift/SKILL.md schedule: "0 9 * * 1" open_pull_request: trueEach run then works on the followed branch, and the draft targets it. The
branch is named secondshift/run-<id>, and the report becomes the description.
A run that fails or changes nothing opens no pull request.
The configuration only asks. A workspace owner allows pull requests in the repository’s settings, and the GitHub App needs write access. Until then, runs still produce their report, and each skipped draft says why. CI waits for a review unless the owner lets it run at once. Tasks explains the rest.
GitHub issues
Section titled “GitHub issues”A job can file what it finds as GitHub issues, such as a weekly review:
jobs: weekly-review: name: Weekly review skill: .agents/skills/weekly-review/SKILL.md schedule: "0 9 * * 1" history: 1 open_issues: labels: [secondshift, weekly-review] max: 5 notify: - binding: team-chatopen_issues: true uses the defaults: no labels and up to 10 issues per run.
| Field | Default | Description |
|---|---|---|
labels |
none | Up to 10 labels added to every issue the job opens. The agent cannot add or change labels. |
max |
10 |
How many issues one run may propose, from 1 to 10. |
The agent gets a propose_issue tool and can propose up to max issues per run.
It never gets write access to GitHub. After a successful run, SecondShift opens
each proposed issue in the job’s repository, with a footer naming the run. It
skips a proposal when an open issue has the same title, so a weekly job does
not file the same finding twice. The run page and the webhook message link
each issue.
The configuration only asks. A workspace owner allows issues in the repository’s settings, and the GitHub App or token needs write access to Issues. Until then, runs still produce their report, and the run says why no issues opened. A failed attempt can be retried from the run page. It opens only the issues that did not open yet. Local repositories cannot receive issues.
Webhook triggers
Section titled “Webhook triggers”A webhook trigger starts the job when a request arrives at its URL, for example from an alerting tool. Name it in the job:
jobs: alert-triage: name: Alert triage skill: .agents/skills/alert-triage/SKILL.md triggers: [webhook]An owner or admin turns the trigger on from the job page. This creates its URL and secret. Copy the secret then. It is not shown again. Neither the URL nor the secret goes in the repository.
Each request must carry the secret in one of two ways:
Authorization: Bearer <secret>. Any scheme works.- A hex HMAC-SHA256 of the body, keyed with the secret, in
X-SecondShift-Signature. Asha256=prefix is allowed. To block replays, also send a Unix timestamp inX-SecondShift-Timestampand sign<timestamp>:<body>. Requests older than five minutes are then refused.
Tools that sign with their own header names usually let you rename them. Set them to the names above.
The alert triage example is a complete job started this way. The Grafana recipe sets Grafana up to call it.
The request body
Section titled “The request body”A run started by a webhook keeps the request body. The skill reads it with the
trigger_payload tool. The body is untrusted data, not instructions. Anyone who
can edit what the sender sends, such as an alert rule’s labels, writes part of
it. A skill should treat it like any other tool result.
The run keeps the first 64 KiB. A longer body is cut, and the run is told so. The body is stored with the run and shown on its Input tab. Do not send secrets in it.
Ignore requests and repeats
Section titled “Ignore requests and repeats”A sender may notify again while an event lasts, and again when it ends. Add a
webhook block so these do not each start a run:
jobs: incident-triage: name: Incident triage skill: .agents/skills/incident-triage/SKILL.md triggers: [webhook] webhook: skip_when: status: resolved repeat_key: incident.id repeat_window: 2hskip_whenignores a request when every listed field has the listed value.repeat_keynames the field that identifies an event. A request with the same value is ignored while that event’s run is active, and forrepeat_windowafter it finished.repeat_windowis a number followed bym,hord, up to7d. It is1hwhen left out.
Fields are the sender’s own. Use a dot for a nested field, such as
labels.team. A request whose body is not JSON, or lacks the field, is never
ignored by these rules. The setup guide for each sender lists the fields to use.
What happens to a request
Section titled “What happens to a request”A request starts a run when all of these hold:
- It carries the secret. Otherwise it gets 401.
- The trigger is on and the job still names it. Otherwise it gets 409.
- The job’s
webhooksettings do not ignore it. An ignored request gets 200, so the sender does not retry it. - The trigger has started fewer runs in the last hour than its cap. The cap is 10 by default. An owner or admin can change it on the job page. Otherwise the request gets 429.
- The job is ready and has no active run. Otherwise it gets 409.
The job page lists recent requests and what happened to each. It keeps the newest 100 of each outcome. A failed sign-in is listed at most once a minute. Turning the trigger off or rotating the secret applies from the next request.
Deliver reports to a webhook
Section titled “Deliver reports to a webhook”Add a logical notification binding to the job:
version: 1
jobs: repository-review: name: Weekly repository review skill: .agents/skills/repository-review/SKILL.md schedule: "0 9 * * 1" timezone: Europe/Warsaw notify: - binding: engineering-reports required: true- Create a webhook connection in SecondShift with the binding
engineering-reportsand your destination URL. - Send a test message to verify delivery.
- Sync the committed manifest. Approve the job’s notification access if the connection requires approval.
- Start a run with Run now. Enable the job when you want it to run on schedule.
The server posts the run status, report summary, and a link to the run in SecondShift after the run finishes. Failed runs are sent too, with the reason they failed. Delivery happens outside the offline sandbox.
The run page shows each delivery under Details as delivered, skipped or failed. If a delivery failed, choose Retry to send the stored report again. The agent does not run again. A retry checks that the job’s notification access is still approved. It can repeat a message the receiver already got, for example after a timeout.
Webhook payload
Section titled “Webhook payload”After each run, the server sends one POST with a JSON body like this:
{ "event": "run.finished", "text": "✅ Weekly review succeeded · acme/web\n\nFound 2 stale setup steps in README.md. Details are in the report.\n\n1 saved file\n\n<https://secondshift.example.com/runs/128|Open run #128>", "run": { "id": 128, "status": "succeeded", "summary": "Found 2 stale setup steps in README.md. Details are in the report.", "commit_sha": "8f3c9d28f3c9d28f3c9d28f3c9d28f3c9d28f3c9", "scheduled_for": "2026-10-26T09:00:00+00:00", "finished_at": "2026-10-26T09:04:00+00:00", "url": "https://secondshift.example.com/runs/128" }, "job": { "key": "weekly-review", "name": "Weekly review" }, "repository": { "full_name": "acme/web" }, "pull_request": null, "issues": null, "outputs": { "changed_files": 0, "saved_files": [{ "name": "stale-steps.csv", "size_bytes": 182 }] }}statusissucceeded,failedorcanceled.summaryis the start of the report, up to 4,000 characters. For a failed run it says why the run failed. The full report stays in SecondShift.textis the same summary with the status and a link, ready for chat. A link in the summary to a saved file, such as[report](/outputs/report.csv), opens that file’s download. Opening it needs a sign-in.urlopens the run in SecondShift. Opening it needs a sign-in.outputscounts the files the agent changed and lists the files it saved, by name and size. Open the run to read or download them.pull_requestholds theurlof the draft a task opened, ornull.textlinks to it too, or says why no draft opened.issueslists the issues a job opened, each with itsnumber,title,urlandalready_open, or isnullwhen the job does not open issues.textlinks to each one, or says why none opened.- No secrets, connection details, tool calls or file contents are sent.
Send test posts a smaller message instead, with event set to
connection.test, a text line and the binding.
The webhook form in the dashboard shows the same sample under What gets sent.
Each notification needs a binding. Its required flag defaults to true.
Set it to false if a missing notification approval should not block the job.
Binding names use lowercase letters, numbers, and single dashes. A job cannot
reuse a binding across its notification, MCP, or API declarations.
Repository configuration must never contain webhook URLs, secrets, credential references, provider endpoints, or OAuth scopes. The workspace owns those settings and can revoke access independently.
MCP and API declarations
Section titled “MCP and API declarations”A job can ask for tools from an MCP server or operations from an HTTP API. The repository names only a logical binding and the tools it needs. The workspace owner connects the service and chooses which tools any job may use. If the connection has Require approval for each job turned on, the owner also approves each job’s request. It is off for new connections: then a job that asks only for allowed tools is ready after sync. See who grants what.
requires: mcps: - binding: github tools: - search_pull_requests - get_commit required: true apis: - binding: billing tools: - listCharges required: truePlace requires inside a job definition. Each entry needs a logical binding
and a nonempty list of unique tools. The required flag defaults to true.
A job with an optional binding (required: false) still runs without it. The
run prompt then names each optional binding the run does not have, with a short
reason: not connected, waiting for approval or denied. The skill can say a
source was not checked, instead of leaving it out. The run’s Input tab shows
the same list.
What a binding can connect to
Section titled “What a binding can connect to”| Connection | Tool names | Sign-in | Scope |
|---|---|---|---|
| GitHub (built in) | search_pull_requests, pull_request_read, list_commits, get_commit, actions_list, actions_get, get_job_logs |
The repository’s own GitHub connection, or a fine-grained personal access token | The job’s own repository |
| Sentry (built in) | find_projects, search_issues, search_errors, search_traces, get_sentry_resource |
Auth token with read scopes | The organization or project chosen when connecting |
| Custom MCP server | The server’s own tool names | OAuth sign-in, or a bearer token | Whatever the credential can reach |
| HTTP API | The operationId of each operation in its OpenAPI 3 spec |
Bearer token, or a named header | Whatever the credential can reach |
GitHub and Sentry offer only read tools that SecondShift allowlists. GitHub Actions tools can list and read workflows, runs, and jobs, and read job logs as text. Logs are capped at the last 300 lines. Triggering runs, artifacts, and log download links are not available. These tools need read-only Actions access, on the token or on the GitHub App. For a custom MCP server, the owner chooses tools after connecting, and tools the server marks read-only start selected. For an HTTP API, GET operations start selected. The access review marks any requested tool that may change data.
Vendor MCP servers has examples for vendors that run an official MCP server.
An operation without an operationId gets a name built from its method and
path, such as get_charges_chargeId. The connection’s edit form lists every
name.
How calls run
Section titled “How calls run”Tool calls never run in the sandbox. The broker makes each call after checking that the job’s grant, the connection, and the tool definitions still match the run’s plan. A change stops the run.
For HTTP APIs, SecondShift builds every request from the spec. The model supplies only the path and query values the spec declares, plus a JSON body for operations that take one. Requests go only to the base URL the owner confirmed when connecting. Redirects are not followed.
The owner can also restrict a tool’s arguments for one job, for example to post only to certain Slack channels. The repository cannot set restrictions. Write the skill so it stays within them: a call that breaks a restriction fails the run. See tool restrictions.
An approved tool can still be misused by a prompt injection. See remaining risks before approving tools that change data.
See the engineering briefing example for a working job that reads live GitHub data.
Need a service that does not fit these options? File an issue describing the service and workflow you want to automate.