Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Declaring tasks

Note

Guide: draft. This works with a source build. The feature is experimental: the constraint set is deliberately small, and it will grow based on what turns out to be needed. Intent binding is the normative source.

Policy says what an agent may ever do in this workspace. A task says what it may do right now, for the work you actually asked for. An action can be refused for not belonging to the task even when policy would allow it.

Why this exists

Suppose your policy lets agents write anywhere in the workspace, which is reasonable. You ask an agent to fix a login bug. It decides the cleanest fix is a database migration and starts writing one. Policy has no opinion, because the policy was written for the repository, not for this request.

A task gives Shiin the missing context, and it comes from you rather than from the model.

Declare one

$ shiin task start "Fix the login bug" \
    --scope './src/auth/**' \
    --scope './tests/auth/**' \
    --forbid db.schema_change \
    --max-writes 20 \
    --minutes 90

Quote the globs, so your shell does not expand them before Shiin sees them.

OptionEffect
--scope <glob>Changes outside these paths are refused. Repeatable
--forbid <action type>These action types are refused. Repeatable
--allow-host <host>Network effects to any other host are refused. *.example.com matches subdomains. Repeatable
--ask-for <action type>These action types always need a person, even when policy allows them. Repeatable
--max-writes <n>Most files the task may write or create
--max-deletes <n>Most files the task may delete
--max-commands <n>Most commands the task may run
--minutes <n>The task ends after this long

What it looks like in use

$ shiin task show
id            0190f3a0-1c2d-4e3f-8a4b-5c6d7e8f9a0b
description   Fix the login bug
scope         ./src/auth/**, ./tests/auth/**
forbidden     db.schema_change
hosts         none
ask for       none
expires       2026-10-12T15:04:00Z
writes        3 of 20
deletes       0 of none
commands      0 of none

A write under src/auth/ proceeds. A write to migrations/0042.sql is refused with reason code deny.intent, and the audit log records which task refused it.

When the work is done:

$ shiin task end

Rules worth knowing

  • A task only ever narrows. It cannot permit something policy refuses. A denial from either one is a denial.
  • Only you can declare a task. An agent cannot start one for itself. If it could, a manipulated agent would simply declare a task that permitted what it wanted.
  • Reads are not scope-limited. A task that stopped an agent from reading the repository would stop it from working. Policy still governs what may be read, which is what keeps secrets shut.
  • A refused action does not spend the budget. Counters move only when an action is allowed.
  • An expired task has ended. Its constraints stop applying, and policy alone decides again. Shiin does not keep enforcing yesterday’s instructions.
  • Natural language is never enforced. The description is recorded for the audit log. Only the constraints are checked, because only they can be checked exactly.

Limits of this

A task constrains the actions Shiin sees. It cannot constrain what a program does once that program is allowed to run, so python cleanup.py is still opaque and still needs a person. See limitations.

Counters live in .shiin/tasks/, which policy cannot grant an agent access to (ADR-0016).