Skip to content
Back to skills

Build Slice

BSecurity

Builds one slice of a sliced Kraft Lite run as a child session that answers to its controller. Use when a controller's starting instructions name this skill, when a session is titled `<chain-id> <key>: <slice title>`, or when a controller or the harness tells a child to continue its slice, to rebase it, or that an earlier slice merged; the controller's side is `kraft-lite:run-slices`.

  • 12 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 11, 2026
ai-agentspythongonodegit

Security analysis

B75/100
  • criticalSends environment variables or credentials to an external URL

Pro shows the line behind each finding and how to fix it

Scanned November 11, 2026

npx -y skills add itsOmidKarami/kraft --skill build-slice --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Build Slice?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Build Slice
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/itsomidkarami-build-slice/badge)](https://www.skillsdirectory.com/skills/itsomidkarami-build-slice)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: build-slice
description: "Builds one slice of a sliced Kraft Lite run as a child session that answers to its controller. Use when a controller's starting instructions name this skill, when a session is titled `<chain-id> <key>: <slice title>`, or when a controller or the harness tells a child to continue its slice, to rebase it, or that an earlier slice merged; the controller's side is `kraft-lite:run-slices`."
---

# Building a slice as a child

If `$CLAUDE_PLUGIN_ROOT` is unset, `kl.py` is two directories above this file;
it needs `python3` 3.10 or newer, and without one, tell the controller and stop.

You are a **child**: one **slice** of a plan, built on your own branch for the
**controller** that started you (its session id is in your starting
instructions). You walk the **child chain**, which `kl.py` derives from the
controller's frozen chain (on the default chain `implementation`,
`verification`, `describe_merge_request`, `draft_merge_request`,
`merge_request_feedback`), with `kraft-lite:next`. The controller keeps the
run's state and decisions log; you write only your own chain.

Your **target** is the branch your PR goes into, as the **Branches and PRs**
item of your starting instructions names it: the feature branch, or in `main`
mode the base branch.

## 1. Setup

Copy this and tick it off; the walk starts when all four are ticked.

```
- [ ] Kraft Lite found: its skills installed, or kl.py reachable by path.
- [ ] Spec and plan joined, written outside git, confirmed to the controller.
- [ ] Fork and base files written, before any commit of your own.
- [ ] Child chain started or resumed, its id in the chain file.
```

1. If `kraft-lite:next` is not installed, install the plugin through the harness;
   failing that, use `python3 <marketplace checkout>/plugins/kraft-lite/kl.py`,
   and read its skills as files.
2. `<run>` is
   `$(git rev-parse --path-format=absolute --git-common-dir)/kraft-lite/<chain-id>`,
   where `<chain-id>` is the controller's chain id, named in the **Slice** item of your
   starting instructions: untracked, readable from any worktree, and the same
   for every slice of the run, so slices sharing a repo write the same files.
   The spec and the plan arrive as messages marked `part i of n`, or as paths on
   the controller's machine. Join the parts in numeric order; ask the controller
   for a missing one. Run `mkdir -p <run>/<slice key>` (it makes `<run>` too),
   then write `<run>/spec.md` and `plan.md`.
   Confirm with a `status` message (section 3): parts joined, line count of each
   file, your plan tasks by their headings.
3. Record where your branch was cut, for section 5. The **Slice** item's line
   gives both: `Cut from: target at <sha>` from the target's tip, where `target`
   is the word itself, never a branch name, or `Cut from: <slice key> at <sha>`
   from that slice's branch. In `<run>/<slice key>/` (yours alone, as worktree
   slices share the repo), write each file only if absent, so a resume keeps it:

   ```
   [ -f <run>/<slice key>/fork ] || echo <sha> > <run>/<slice key>/fork
   [ -f <run>/<slice key>/base ] || echo <the word after "Cut from:", as given> > <run>/<slice key>/base
   ```

   `base` holds the word `target` or a slice key, never a branch name. With no
   `Cut from:` line and no commits of your own, write `target` and
   `git rev-parse HEAD`; with commits of your own, never `HEAD`: ask the
   controller for the line.
4. Your child chain's id lives in `<run>/<slice key>/chain`, never in the
   `chains` list, which shows every chain in a shared store, siblings included.
   If the file holds an id, resume that chain. If it is absent, start it from
   the controller's frozen chain, exported to the state branch your **Slice**
   item names:

   ```
   git fetch origin <state branch>
   git show FETCH_HEAD:kraft-lite-state/chain.json > <run>/chain.json
   python3 "$CLAUDE_PLUGIN_ROOT/kl.py" start --slice-of <run>/chain.json --title "<slice title>"
   echo <id> > <run>/<slice key>/chain
   python3 "$CLAUDE_PLUGIN_ROOT/kl.py" state --chain-id <id>
   ```

   `<id>` is the `chain_id` that `start` prints. `state` must show the child
   chain's first node (`implementation` on the default chain), `open`; if not,
   the checkout you ran it in or the id is wrong, so start no other chain. A
   refused `start` goes to the controller as a `question` with the refusal in
   full (a refused binding opens as section 2 says); never start with `--chain`
   instead.
5. Invoke `kraft-lite:next`. Pass your child chain's `--chain-id <id>` to every
   verb except `settings`, `import`, `chains`, `detect`, `migrate` and `start`.
   On every wake-up, restore or compaction, read the id from
   `<run>/<slice key>/chain` before you invoke it: `next`'s advice to take the
   id from `chains`, or to put the choice of chain to the user, isn't for a
   child.

### A fresh container

`<run>/<slice key>/` is missing while your branch on the remote already has
commits of your own: the container was replaced, and the chain state and the
documents went with it. The pushed commits are the work done.

1. Put your branch at its pushed head before anything else:
   `git fetch origin`, then `git checkout -B <your slice branch> origin/<your slice branch>`.
   A fresh clone can check out an older revision, and the fork checks and
   `--force-with-lease` would then not see the pushed work.
2. Send the controller a `status`: fresh container, your pushed head and PR.
   Ask it for the spec and the plan again.
3. Run setup. `fork` and `base` come from the latest `Cut from:` line here (your
   starting instructions or your last rebase `status`), never from `HEAD`.
4. At the new child chain's first build node, build only the plan tasks your
   pushed commits do not hold. Never redo, reset or force-push away pushed work.

## 2. The controller takes the user's place

**You never put a question to the user. Every question goes to the controller,
including when the user is in your session and offers to answer.** Wherever
`kraft-lite:next`, `kraft-lite:gate` or a fix-loop cap would stop for the user,
send the controller a `question` (section 3) and keep working on whatever does
not depend on the answer.

Configuration is the one kind of question the user decides: a binding in
`.kraft-lite/bindings.yaml` that `start` or `state` refuses. Send the controller
a `question` opening `For the user: configuration` that names the task, its
binding and the refusal; it tells the user. You rebind nothing, substitute
nothing, and put no question to the user.

When the user speaks to you, reply with status only, from your own
`state --chain-id <id>`, not `kraft-lite:status`, in one line: `Slice <key> is at
<node>; I asked the controller <what>; meanwhile I <what>.` Invite no answer, even one meant for the controller. An
instruction or answer they volunteer goes to the controller, verbatim in a
`status`; the controller decides what it changes. When the user types
`You may push to <branch>.` for a push you asked the controller about, that is
the permission: push, then send the controller a `status`.

| Thought | Reality |
|---|---|
| "The user offered, and they are who I work for" | An answer given to you is not in the decisions log, and the controller may have decided otherwise. Ask the controller. |
| "The controller has been silent" | Silence is no leave to put it to the user. Send it again on the next channel and keep working on the rest. |
| "I'll just say: if you want to steer it, tell the controller" | That is still an invitation to answer. Say where the slice is and that you asked, and stop. |
| "It is only a plan gap; I'll pick and flag it" | A gap the plan leaves open is the controller's to close, at the cost of one message. |

Red flags - send the question to the controller instead: you are about to write
the user "if you know", "tell me and I'll pass it on", "tell the controller", or
any question.

## 3. Channels and messages

Try in this order, and read answers in the same order each time you wake:

1. A session message to the controller (locally, addressed by the name your
   starting instructions give).
2. On the same machine, one JSON line appended to
   `.kraft-lite/messages/<chain-id>/<slice key>.jsonl` under the controller's
   checkout path in your starting instructions: `{"at", "from": "child", "kind",
   "body"}`, where `<chain-id>` is the controller's chain id, never your child chain's.
3. A comment on your own draft PR into the target: open it at your first commit
   if none is open (`mr.open_draft` later reports the one already open).

`kind` is `question`, `status` or `done`; the controller answers with `answer`.
Every message has these parts, in this order:

```
<kind> | <slice key> | <node>
<what happened or what stopped, one line>
<evidence, in full>
<what you would do, and what you do meanwhile>
```

When `attempt` prints `over_cap: true`, stop the loop and send a `question`
whose evidence is every attempt's output in full. Run no further repair, no
further `attempt`, no `close` until the controller answers; then follow its
steer. It re-steers you once or holds the question for the user. After a
re-steer, run the repair it names: `attempt` still prints `over_cap: true`, and
that alone is no reason to stop again. Stop only if the steered round fails, and
send that round's output as a new `question`. `close` stays barred until the
node passes.

When no work is left that doesn't wait on an answer, arm the wake-up as section 4
says and end the turn. Don't poll the controller or put a question to the user.

## 4. CI

At the node whose `mr.ci` task reports the checks (`merge_request_feedback` on
the default chain), checks still running are not a failure. Send the `status`
below, arm a wake-up (the scheduling tool your **Controller** item names: a
message to yourself for later, saying you will run `kraft-lite:next` when it
arrives), and end the turn. Where it names none, end the turn: the controller's check-in
messages you to continue. Ask no one to run a skill or a command for you, the
user included.

```
status | <slice key> | <node>
CI pending: <PR link>
Checks: <name: state, ...>
Next: <wake-up time, or "none armed; continue on the controller's check-in">
```

## 5. Rebasing

The target squash-merges, so an earlier slice's own commits never reach it.
`<run>/<slice key>/base` says when you rebase, and `<onto>` is what onto:

| `base` holds | Rebase when | `<onto>` | When `kraft.mr_rebase` runs |
|---|---|---|---|
| `target` | `kraft.mr_rebase` runs, or the controller says to | `origin/<target branch>` | rebase |
| a slice key | the controller says that slice merged | `origin/<target branch>` | nothing to do: that slice's commits are on your branch and not yet on the target, so a rebase would drop them |
| a slice key | the controller says to rebase onto that slice's new tip | `origin/<that slice's branch>` | as above |

To rebase:

1. Commit uncommitted work (a WIP commit). Never stash: the stash is shared by
   every worktree of the repo, sibling slices included.
2. `git fetch origin`, then check the fork. If either check fails, run no
   rebase: send the controller a `question` with both shas, and keep working.
   - `git merge-base --is-ancestor "$(cat <run>/<slice key>/fork)" HEAD` succeeds.
   - The fork is not `HEAD` itself while your branch has commits of your own.
3. `git rebase --onto <onto> "$(cat <run>/<slice key>/fork)"`, with `<onto>` a
   remote branch, never a sha from a message. That commit is where your branch
   was cut, so this works after a start from a slice branch that was since
   force-pushed or deleted. Once it succeeds, record where you are now cut:
   `git rev-parse <onto> > <run>/<slice key>/fork`, and onto the target branch
   `echo target > <run>/<slice key>/base` (the word `target`). Onto a slice's
   new tip, `base` keeps that slice's key. On a conflict in your own files,
   resolve it. In a file another slice owns, `git rebase --abort`, send the
   controller a `question`, and keep working. Never `git merge` or `-X` instead.
4. Re-run your scoped tests; if they fail, fix and re-run before you push. Then
   `git push --force-with-lease origin <your slice branch>`.
5. Send a `status` with the new head, the test result, and the line
   `Cut from: target at <the new fork>`, or onto a slice's new tip
   `Cut from: <slice key> at <the new fork>`.

## 6. Pushes, tests and review

Push only your own slice branch, and open the PR into the target. Any other
push is the user's: send the controller a `question` naming the branch. It
tells the user to type `You may push to <branch>.` here; push only after those
words appear in this session. The controller's word is not them.

The first build node's tasks run under the slice layer (their `source` in
`state` says so) and end after the last plan task's own reviews, whatever they
are bound to: no final whole-branch review and no finishing, merge or PR step
inside them, and no question to the user about one. When the bound skill offers
either, close the first build node instead: the child chain's review task and
its MR node do those. Every other task runs as `state` dispatches it; the PR
body lists the paths `kraft.verify_changed_test_scopes` ran, and says CI runs
the full suite.

## 7. Done

When `state` reports `done`, send the controller this and stop:

```
done | <slice key> | <your child chain's last node>
PR: <link>
Deviations from the plan: <each, or "none">
Look at first: <file or behaviour>
```

Findings from the controller's review after `done`: fix them on your own
branch, re-run the scoped tests, push, then send `done` again with the new head
and what changed. Run no chain verb: the chain is finished.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

View all in ai-agents →

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.