# Change a prompt or a setting without a deploy

> Keep an agent's prompt and settings as data blocks, so an expert changes them and makes a new agent version, with no code change.

This guide moves an agent's prompt and settings into data blocks, so the people who know the work change them and make a new agent version, with no code change and no deploy.

A **data block** is a prompt, or a set of settings, kept in the runtime instead of in your code. Each change is a new version of the block, and an agent version pins the block versions it uses. So the people who know the work can change a prompt or a limit and make a new agent version from it: no code change, and no deploy.

There are two kinds:

- **A prompt block** is an agent's instructions: a Liquid template, with the parameters it takes, rendered as the agent's [instructions](https://docs.kindgi.com/guides/agents/give-an-agent-input.md) are.
- **A settings block** is a JSON object of values, with an optional JSON Schema they must satisfy. The agent's instructions and its tools read them.

## Before you start

- **An agent to change,** in a pack on a runtime, with Kindgi 0.2's CLI. This guide uses the reply drafter from [Give an agent its input](https://docs.kindgi.com/guides/agents/give-an-agent-input.md). Under `kindgi dev`, blocks come only from the pack's files, as [Keep them in the pack](https://docs.kindgi.com/guides/improve/change-a-prompt-or-setting.md#keep-them-in-the-pack) shows. The publish commands on this page are for a runtime that isn't `kindgi dev`, such as one you deploy to: `kindgi dev` refuses them.
- **The project's id:** `kindgi projects get-default` prints the default project, with its `id`.
- **For the app's half:** Node 22.12 or later, Python 3.11 or later, or Java 17 or later, with Kindgi 0.2's client, and `KINDGI_API_URL` and `KINDGI_API_TOKEN` set.

## Publish the blocks

The examples on this page move the reply drafter's instructions from [Give an agent its input](https://docs.kindgi.com/guides/agents/give-an-agent-input.md) into a prompt block, `acme.reply-prompt`, and add a settings block, `acme.reply-style`: how long a reply may be, and up to what amount a refund needs no approval.

A prompt block with parameters is published from a JSON file. Ask your coding agent to write the prompt block:

```text
I want my agent's instructions in a prompt block, with the tone as a parameter.
```

For reference, the block it writes looks like this. This guide saves it as `reply-prompt.json`:

```json excerpt
{
  "id": "acme.reply-prompt",
  "version": "1.0.0",
  "kind": "prompt",
  "description": "The reply drafter's instructions",
  "content": {
    "template": "You draft short replies to Acme customers, in a {{ tone }} tone, in at most {{ settings[\"acme.reply-style\"].maxSentences }} sentences.\nWhen a customer asks for money back, check the amount with the refund check tool, and say whether a person needs to approve the refund.\nSign every reply as {{ signature }}.",
    "parameters": [
      { "name": "tone", "type": "string", "description": "friendly, formal, …" },
      { "name": "signature", "type": "string", "default": "The Acme team" }
    ]
  }
}
```

Publish it, and the settings block:

- **TypeScript**

  ```sh
  pnpm exec kindgi blocks publish --project=<project-id> @reply-prompt.json
  pnpm exec kindgi blocks publish acme.reply-style --block-version=1.0.0 --project=<project-id> --settings='{"maxSentences":3,"refundLimit":50}' --schema=@reply-style.schema.json
  ```

- **Python**

  ```sh
  uv run kindgi blocks publish --project=<project-id> @reply-prompt.json
  uv run kindgi blocks publish acme.reply-style --block-version=1.0.0 --project=<project-id> --settings='{"maxSentences":3,"refundLimit":50}' --schema=@reply-style.schema.json
  ```

- **Java**

  ```sh
  ./kindgiw blocks publish --project=<project-id> @reply-prompt.json
  ./kindgiw blocks publish acme.reply-style --block-version=1.0.0 --project=<project-id> --settings='{"maxSentences":3,"refundLimit":50}' --schema=@reply-style.schema.json
  ```

- **Scala**

  ```sh
  ./kindgiw blocks publish --project=<project-id> @reply-prompt.json
  ./kindgiw blocks publish acme.reply-style --block-version=1.0.0 --project=<project-id> --settings='{"maxSentences":3,"refundLimit":50}' --schema=@reply-style.schema.json
  ```

* `--project=<project-id>` is the project the block lives in, where `<project-id>` is its id.
* `@reply-prompt.json` reads the whole block from the file.
* `acme.reply-style` and `--block-version` are the settings block's id and version.
* `--settings` is its values, as JSON.
* `--schema=@reply-style.schema.json` reads the JSON Schema its values must satisfy from a file.

Each prints the block's id and version. The second prints:

```json
{
  "blockId": "acme.reply-style",
  "version": "1.0.0"
}
```

- **The template reads a settings block** as `{{ settings["acme.reply-style"].maxSentences }}`: the block's id in quotes, then the key.

- **A settings block's schema** checks its values, and later versions' too. A version published without one keeps the latest version's. A version that gives one replaces it, and `--schema='{}'` drops the check on purpose. Values that don't fit are refused:

  ```text
  Block "acme.reply-style" can't take this version
    ✗ /content/values/refundLimit: must be >= 0 (the schema of version 1.0.0)
  ```

`kindgi blocks list`, `show` and `versions` read them. `unregister` and `reinstate` retire a version and bring it back.

### Keep them in the pack

A pack can bring its blocks instead: one JSON file per block in its `blocks/` folder, in the same shape as `reply-prompt.json`. `kindgi deploy` publishes them into the project it deploys to, before the agents that use them, so a block and its agent ship together. Ask your coding agent: "Keep my agent's blocks in the pack."

```text
blocks/
  reply-prompt.json
  reply-prompt.liquid
  reply-style.json
```

- **A settings block's file** holds its `values`, and its `schema` when it has one, under `content`.
- **A long template can have its own file,** named by `templateFile` in place of `template`: `"templateFile": "reply-prompt.liquid"`. The path is from the block's file, and stays inside the blocks folder. The build reads the file into the block. Spell it as the file on disk: a Linux build tells `Reply.liquid` and `reply.liquid` apart.
- **`kindgi build` checks each block,** its template and its values against its schema, and refuses a pack whose blocks don't hold up, naming each file. `kindgi dev` shows the same problems as you edit, and its runtime reads blocks from these files only. When a Python, Java or Scala pack's `kindgi` package or `com.kindgi:kindgi-pack` is from before blocks in a pack, and so reads none, `kindgi build` refuses the pack and `kindgi dev` says so, each naming the release to upgrade to.
- **The folder** is the `discovery.blocks` pattern in `kindgi.config.ts` (`kindgi.config.json` in a Java or Scala pack; `blocks` under `[tool.kindgi.discovery]` in a Python pack's `pyproject.toml`), `blocks/**/*.json` unless you change it.

A version never changes. When a deploy brings a version the runtime already holds:

- **With the same content,** it keeps it, so a deploy whose blocks didn't change publishes none.

- **With other content,** the deploy is refused: give the block a new version in its file.

- **Unregistered,** by `kindgi blocks unregister` or by a deploy that failed after publishing it, the deploy is refused, naming the command that reinstates each such block:

  ```text
  kindgi blocks reinstate acme.reply-prompt --block-version=1.0.0
  ```

An agent pins the pack's own version of each block the pack brings, as the pack's files are the agent: a newer version published on the runtime since isn't picked, even one in the agent's range. If the agent's range doesn't take the pack's version, the deploy is refused, naming both. A block the pack doesn't bring pins the newest published version in the agent's range, as before. A runtime from an earlier release doesn't publish a pack's blocks: `kindgi deploy` checks first, and refuses, naming the release it needs.

## Use them in an agent

The agent names its prompt block and its settings blocks by range, as it names its tools. Ask your coding agent: "Have my agent use its prompt block and settings block." For reference, the agent looks like this:

- **TypeScript**

  `agents/reply-drafter/index.ts`

  ```ts
  import { defineAgent } from '@kindgi/sdk/define';
  import type { AgentId, Semver } from '@kindgi/sdk/types';

  const defined = defineAgent({
    id: 'acme-ops.reply-drafter' as AgentId,
    version: '1.0.0' as Semver,
    name: 'Reply drafter',
    instructions: { prompt: 'acme.reply-prompt', version: '^1.0.0' },
    settings: [{ id: 'acme.reply-style', version: '^1.0.0' }],
    capabilities: [{ needs: [{ feature: 'tool-use' as const }] }],
    tools: [{ id: 'acme-ops.refund-check', version: '^0.1.0' }],
    retrieval: [],
    guardrails: [],
  });

  if (defined.kind === 'err') throw new Error(defined.error.message);
  export default defined.value;
  ```

- **Python**

  `agents/reply_drafter.py`

  ```python
  from kindgi import Agent

  from ..tools.refund_check import refund_check

  reply_drafter = Agent(
      id="acme-ops.reply-drafter",
      version="1.0.0",
      name="Reply drafter",
      instructions={"prompt": "acme.reply-prompt", "version": "^1.0.0"},
      settings=[{"id": "acme.reply-style", "version": "^1.0.0"}],
      capabilities=[{"needs": [{"feature": "tool-use"}]}],
      tools=[refund_check],
  )
  ```

- **Java**

  `src/main/java/acmeops/agents/ReplyDrafter.java`

  ```java
  package acmeops.agents;

  import acmeops.tools.RefundCheck;
  import com.kindgi.pack.Agent;
  import java.util.List;
  import java.util.Map;

  public final class ReplyDrafter {
    public static final Agent AGENT = Agent.define("acme-ops.reply-drafter")
        .version("1.0.0")
        .name("Reply drafter")
        .instructions(Map.of("prompt", "acme.reply-prompt", "version", "^1.0.0"))
        .set("settings", List.of(Map.of("id", "acme.reply-style", "version", "^1.0.0")))
        .capability(Map.of("needs", List.of(Map.of("feature", "tool-use"))))
        .tool(RefundCheck.TOOL)
        .build();

    private ReplyDrafter() {}
  }
  ```

- **Scala**

  `src/main/scala/acmeops/agents/ReplyDrafter.scala`

  ```scala
  package acmeops.agents

  import acmeops.tools.RefundCheck
  import com.kindgi.pack.scaladsl._

  object ReplyDrafter {
    val agent: Agent = Agent("acme-ops.reply-drafter")
      .version("1.0.0")
      .name("Reply drafter")
      .instructions(Map("prompt" -> "acme.reply-prompt", "version" -> "^1.0.0"))
      .set("settings", Seq(Map("id" -> "acme.reply-style", "version" -> "^1.0.0")))
      .capability(Map("needs" -> Seq(Map("feature" -> "tool-use"))))
      .tool(RefundCheck.tool)
      .build()
  }
  ```

The prompt block brings its parameters, so the agent declares none. A tool reads the settings blocks of the agent version that calls it, as `ctx.settings`. Ask your coding agent: "I want a tool that says whether a refund needs a person's approval, with the limit from my settings block." For reference, the tool looks like this:

- **TypeScript**

  `tools/refund-check/index.ts`

  ```ts
  import { defineTool } from '@kindgi/sdk/define';
  import type { ToolId } from '@kindgi/sdk/types';
  import { z } from 'zod';

  const defined = defineTool({
    id: 'acme-ops.refund-check' as ToolId,
    description: 'Says whether a refund of this amount needs a person to approve it.',
    version: '0.1.0',
    input: z.object({ amount: z.number().positive() }),
    output: z.object({ needsApproval: z.boolean(), limit: z.number() }),
    effects: [],
    mutating: false,
    handler: async (input, ctx) => {
      // The agent version's settings block: an expert changes the limit, not this code.
      const limit = Number(ctx.settings?.['acme.reply-style']?.refundLimit ?? 0);
      return { needsApproval: input.amount > limit, limit };
    },
  });

  if (defined.kind === 'err') throw new Error(defined.error.message);
  export default defined.value;
  ```

- **Python**

  `tools/refund_check.py`

  ```python
  from pydantic import BaseModel, Field

  from kindgi import ToolContext, tool


  class RefundAmount(BaseModel):
      amount: float = Field(gt=0)


  class RefundCheck(BaseModel):
      needs_approval: bool = Field(alias="needsApproval")
      limit: float


  @tool(id="acme-ops.refund-check", mutating=False)
  def refund_check(refund: RefundAmount, ctx: ToolContext) -> RefundCheck:
      """Says whether a refund of this amount needs a person to approve it."""
      # The agent version's settings block: an expert changes the limit, not this code.
      limit = float(ctx.settings.get("acme.reply-style", {}).get("refundLimit", 0))
      return RefundCheck(needsApproval=refund.amount > limit, limit=limit)
  ```

- **Java**

  `src/main/java/acmeops/tools/RefundCheck.java`

  ```java
  package acmeops.tools;

  import com.kindgi.pack.Tool;
  import jakarta.validation.constraints.Positive;
  import java.util.Map;

  public final class RefundCheck {
    public record Input(@Positive double amount) {}

    public record Output(boolean needsApproval, double limit) {}

    public static final Tool<Input, Output> TOOL = Tool.define("acme-ops.refund-check")
        .description("Says whether a refund of this amount needs a person to approve it.")
        .version("0.1.0")
        .input(Input.class)
        .output(Output.class)
        .mutating(false)
        .handler((input, ctx) -> {
          // The agent version's settings block: an expert changes the limit, not this code.
          Map<String, Object> style = ctx.settings().getOrDefault("acme.reply-style", Map.of());
          double limit = style.get("refundLimit") instanceof Number n ? n.doubleValue() : 0;
          return new Output(input.amount() > limit, limit);
        });

    private RefundCheck() {}
  }
  ```

  `ctx.settings()` maps each settings block the calling agent version pins, by id, to its values.

- **Scala**

  `src/main/scala/acmeops/tools/RefundCheck.scala`

  ```scala
  package acmeops.tools

  import com.kindgi.pack.scaladsl._
  import jakarta.validation.constraints.Positive

  object RefundCheck {
    final case class Input(@Positive amount: Double)
    final case class Output(needsApproval: Boolean, limit: Double)

    val tool: Tool[Input, Output] = Tool[Input, Output]("acme-ops.refund-check")
      .description("Says whether a refund of this amount needs a person to approve it.")
      .version("0.1.0")
      .readOnly
      .handler { (input, ctx) =>
        // The agent version's settings block: an expert changes the limit, not this code.
        val limit = Option(ctx.settings.get("acme.reply-style")).flatMap(s => Option(s.get("refundLimit"))) match {
          case Some(n: Number) => n.doubleValue
          case _ => 0.0
        }
        Output(needsApproval = input.amount > limit, limit = limit)
      }
  }
  ```

  `ctx.settings` maps each settings block the calling agent version pins, by id, to its values (a Java map).

A run passes the prompt's parameters as it does an agent's own:

- **TypeScript**

  ```sh
  pnpm exec kindgi runs start --agent=acme-ops.reply-drafter --input='{"userMessage":"I was charged twice for my $80 order. Can I get my money back?","parameters":{"tone":"friendly"}}'
  ```

- **Python**

  ```sh
  uv run kindgi runs start --agent=acme-ops.reply-drafter --input='{"userMessage":"I was charged twice for my $80 order. Can I get my money back?","parameters":{"tone":"friendly"}}'
  ```

- **Java**

  ```sh
  ./kindgiw runs start --agent=acme-ops.reply-drafter --input='{"userMessage":"I was charged twice for my $80 order. Can I get my money back?","parameters":{"tone":"friendly"}}'
  ```

- **Scala**

  ```sh
  ./kindgiw runs start --agent=acme-ops.reply-drafter --input='{"userMessage":"I was charged twice for my $80 order. Can I get my money back?","parameters":{"tone":"friendly"}}'
  ```

`--input`'s `parameters` are the prompt's parameters: here, `tone`. The run's `appended` messages show the tool reading the settings block's limit:

```json excerpt
"appended": [
  { "role": "user", "content": "I was charged twice for my $80 order. Can I get my money back?", … },
  { "role": "agent", "content": { "text": "", "toolCalls": [{ "name": "acme-ops.refund-check", "arguments": { "amount": 80 }, … }] }, … },
  { "role": "tool", "content": { "limit": 50, "needsApproval": true }, … },
  { "role": "agent", "content": "We're so sorry you were charged twice—that's definitely a mistake on our end! The good news is we can process your $80 refund, but it will need approval from one of our team members, so you should see it within a couple of business days. Thanks for your patience!\n\nThe Acme team", … }
]
```

The run's journal, which `kindgi runs journal <run-id>` prints, has the versions it used, in its `setup` step. It has the instructions as rendered, in `render-prompt`:

```text
{ "blockVersions": { "prompts": { "acme.reply-prompt": "1.0.0" }, "settings": { "acme.reply-style": "1.0.0" } } }
{ "rendered": "You draft short replies to Acme customers, in a friendly tone, in at most 3 sentences.\nWhen a customer asks for money back, check the amount with the refund check tool, and say whether a person needs to approve the refund.\nSign every reply as The Acme team." }
```

**Under `kindgi dev`,** each run uses the version in the pack's file, so a change you save is used at the next run. A block that isn't in the pack, or whose version the agent's range doesn't take, fails the run, naming the block and saying to add it under the pack's blocks folder. A block published to the runtime some other way isn't used: move it into `blocks/`.

**A published agent version pins them.** When it's published, by `kindgi deploy` or the API, each range resolves once, like its tools', as [Agent and tool versions](https://docs.kindgi.com/guides/agents/agent-and-tool-versions.md) describes:

```json excerpt
{
  "version": "1.0.0",
  "pins": {
    "tools": { "acme-ops.refund-check": "0.1.0" },
    "prompts": { "acme.reply-prompt": "1.0.0" },
    "settings": { "acme.reply-style": "1.0.0" }
  },
  "pinsDigest": "sha256:3a7b0e31e9b6fb9149ea98dc3d0be451512e9d765f1321a834a5a4fad2fffe09",
  …
}
```

A block version that's missing or of the wrong kind refuses the publish, naming each. So do model settings with values a model can't take: `Agent "acme-ops.reply-drafter" uses tool or data-block versions it can't pin (1 issue)`.

**Model settings** are a settings block the agent names as `modelSettings`, such as `{ "temperature": 0.2, "maxOutputTokens": 400, "thinking": "low" }`. A temperature goes from 0 to 2. `thinking` is how much the model thinks: `lowest`, `minimal`, `low`, `medium`, `high`, `xhigh` or `max`. Its model calls use them, and the version pins the block with its other settings. The block can also name the model the turn prefers, `model` and `providerId`, in place of the agent's `preferredModel` and `preferredProvider` ([never both](https://docs.kindgi.com/guides/agents/choose-a-model.md#prefer-a-provider-or-a-model)).

- **A reply that reaches `maxOutputTokens` stops there,** mid-sentence if need be, and the run still completes.
- **Some models take no temperature,** such as the Claude 5.5 and GPT-6 models. Their calls go without it, and the turn carries a `sampling-unsupported` warning, as [Temperature and thinking](https://docs.kindgi.com/guides/models.md#temperature-and-thinking) describes.
- **A model that thinks** spends part of `maxOutputTokens` on thinking. A level it doesn't take goes as the next one up that it takes, or its highest, with a `thinking-level-adjusted` warning. Without `thinking`, its calls get the model's registered `default`, or else the vendor's, as [Temperature and thinking](https://docs.kindgi.com/guides/models.md#temperature-and-thinking) describes.

## Change a block

A change is a new version of the block, so the people who know the work change the values, not the code. With the CLI:

- **TypeScript**

  ```sh
  pnpm exec kindgi blocks publish acme.reply-style --block-version=1.1.0 --project=<project-id> --settings='{"maxSentences":3,"refundLimit":100}'
  ```

- **Python**

  ```sh
  uv run kindgi blocks publish acme.reply-style --block-version=1.1.0 --project=<project-id> --settings='{"maxSentences":3,"refundLimit":100}'
  ```

- **Java**

  ```sh
  ./kindgiw blocks publish acme.reply-style --block-version=1.1.0 --project=<project-id> --settings='{"maxSentences":3,"refundLimit":100}'
  ```

- **Scala**

  ```sh
  ./kindgiw blocks publish acme.reply-style --block-version=1.1.0 --project=<project-id> --settings='{"maxSentences":3,"refundLimit":100}'
  ```

`--block-version=1.1.0` is the new version, and `--settings` its values: a higher refund limit. It prints the block's id and version.

Each version records who published it, `publishedBy`, and where it came from, `source`: `{ "kind": "api" }` for a publish like this one, a deployment's id for a version a deploy brought, and a suggested change's id for one that came from a suggested change. `GET /v1/blocks/<block-id>/versions/<version>` answers both, and the audit log records the publish with you as its actor. A version published before this was recorded has neither.

A new version of a block that an agent version uses as its model settings must be model settings, and can't name the model that agent version names. Otherwise the publish is refused, naming the agent version.

From your code, such as an admin page of your own, ask your coding agent: "I want to publish a new version of my settings block from my app." For reference, the code looks like this:

- **TypeScript**

  `publish-block.ts`

  ```ts
  import { createClient } from '@kindgi/sdk/client';

  const kindgi = createClient(); // KINDGI_API_URL, KINDGI_API_TOKEN, or the running kindgi dev
  const project = await kindgi.projects.getDefault();

  // The settings block's next version: a higher refund limit, and no code change.
  await kindgi.blocks.publish(
    {
      id: 'acme.reply-style',
      version: '1.1.0',
      kind: 'settings',
      content: { values: { maxSentences: 3, refundLimit: 100 } },
    },
    { projectId: project.id },
  );

  const versions = await kindgi.blocks.versions.list('acme.reply-style');
  console.log(versions.data.map((block) => block.version));
  ```

- **Python**

  `publish_block.py`

  ```python
  from kindgi.client import Kindgi

  kindgi = Kindgi()  # KINDGI_API_URL, KINDGI_API_TOKEN, or the running kindgi dev
  project = kindgi.projects.get_default()

  # The settings block's next version: a higher refund limit, and no code change.
  kindgi.blocks.publish(
      project_id=project.id,
      id="acme.reply-style",
      version="1.1.0",
      kind="settings",
      content={"values": {"maxSentences": 3, "refundLimit": 100}},
  )

  versions = kindgi.blocks.versions.list("acme.reply-style")
  print([block.version for block in versions.data])
  ```

- **Java**

  `src/main/java/com/acme/app/PublishBlock.java`

  ```java
  package com.acme.app;

  import com.kindgi.client.Kindgi;
  import com.kindgi.client.models.Block;
  import com.kindgi.client.models.BlockKind;
  import com.kindgi.client.models.Project;
  import com.kindgi.client.models.PublishBlockBody;
  import com.kindgi.client.models.SettingsBlockContent;
  import java.util.Map;
  import java.util.UUID;

  public final class PublishBlock {
    public static void main(String[] args) {
      Kindgi kindgi = Kindgi.create(); // KINDGI_API_URL, KINDGI_API_TOKEN, or the running kindgi dev
      Project project = kindgi.projects().getDefault();

      // The settings block's next version: a higher refund limit, and no code change.
      kindgi.blocks().publish(PublishBlockBody.builder()
          .projectId(UUID.fromString(project.id()))
          .id("acme.reply-style")
          .version("1.1.0")
          .kind(BlockKind.SETTINGS)
          .content(SettingsBlockContent.builder().values(Map.of("maxSentences", 3, "refundLimit", 100)).build())
          .build());

      System.out.println(kindgi.blocks().versions().list("acme.reply-style").data().stream()
          .map(Block::version).toList());
    }
  }
  ```

  It prints the versions below without quotes: `[1.1.0, 1.0.0]`.

- **Scala**

  `src/main/scala/acme/app/PublishBlock.scala`

  ```scala
  package acme.app

  import com.kindgi.client.Kindgi
  import com.kindgi.client.models.{BlockKind, PublishBlockBody, SettingsBlockContent}
  import java.util.UUID
  import scala.jdk.CollectionConverters._

  object PublishBlock {
    def main(args: Array[String]): Unit = {
      val kindgi = Kindgi.create() // KINDGI_API_URL, KINDGI_API_TOKEN, or the running kindgi dev
      val project = kindgi.projects().getDefault()

      // The settings block's next version: a higher refund limit, and no code change.
      kindgi.blocks().publish(PublishBlockBody.builder()
        .projectId(UUID.fromString(project.id))
        .id("acme.reply-style")
        .version("1.1.0")
        .kind(BlockKind.SETTINGS)
        .content(SettingsBlockContent.builder()
          .values(Map[String, AnyRef]("maxSentences" -> Int.box(3), "refundLimit" -> Int.box(100)).asJava)
          .build())
        .build())

      println(kindgi.blocks().versions().list("acme.reply-style").data.asScala.map(_.version).mkString("[", ", ", "]"))
    }
  }
  ```

  It prints the versions below without quotes: `[1.1.0, 1.0.0]`.

It prints the block's versions, newest first:

```text
[ '1.1.0', '1.0.0' ]
```

Under `kindgi dev`, change the block's file instead: its `version` and its values. `kindgi dev` reloads it, the next run reads 1.1.0, and its tool gets `{ "limit": 100, "needsApproval": false }`. A publish there is refused: `Error [registry-read-only]: Under kindgi dev, the pack is the source of data blocks: change the block in the pack's files and kindgi dev reloads it.`

## Make an agent version that runs it

A new block version doesn't change a published agent version: it keeps running the versions it pins. An agent version that runs new content comes one of two ways:

- **A suggested change,** for one project: it takes the block's new values or instructions, publishes the block version, makes the agent version that pins it, and tries both agent versions on the project's test cases. It needs no code change and no deploy. [Suggest a change, and test it](https://docs.kindgi.com/guides/improve/suggest-a-change.md) says how.
- **A deploy:** reference the new block version from the agent in the pack, and `kindgi deploy`.

A developer publishing from code a number a suggested change's version took is told which one is free: `Agent "acme-ops.reply-drafter" version "1.0.1" is already registered, and versions never change; publish it as 1.0.2, the next free version`.

An agent version that pins another settings block version than the one it came from runs read-only tools such as `acme-ops.refund-check` live on the test cases, rather than reusing the recorded results. So the new refund limit shows in the answers. Those cases count as `diverged`, which a release gate with `replay` and `maxDiverged: 0` refuses.

## In the console

A project's **Prompts & settings** section lists its blocks. A block's page shows its template or values, its versions with **Retire**, and the agents using it.

1. **Edit as a new version** starts from the version you're viewing and proposes the next number, with an optional note for this version. Published versions never change: this publishes a new one.
2. After it's published, **Derive a version** next to an agent using the block makes the agent version that runs it: "Derive a new version of Reply drafter with acme.reply-prompt 1.0.1?"

An agent's page says where its instructions and settings come from, and where a derived version came from: "Derived from 1.0.0 with new prompt or settings versions ("Refund limit 100")".

Under `kindgi dev`, a block's page offers no edit and no derive: change the block's file instead, with a new version. Or suggest a change, yourself with **Suggest a change** or with an Improve run: pinning it writes it into the file ([Under `kindgi dev`](https://docs.kindgi.com/guides/improve/suggest-a-change.md#under-kindgi-dev)).

## Next

- [Try a version on the test cases](https://docs.kindgi.com/guides/improve/compare-an-agent-version.md)
- [Release an agent version](https://docs.kindgi.com/guides/improve/release-an-agent-version.md)
- [Suggest a change, and test it](https://docs.kindgi.com/guides/improve/suggest-a-change.md)
