Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 0 additions & 137 deletions src/decks/actions/internal_knowledge_read.deck.ts

This file was deleted.

6 changes: 0 additions & 6 deletions src/decks/actions/internal_knowledge_read/PROMPT.md

This file was deleted.

85 changes: 85 additions & 0 deletions src/decks/actions/policy_read.deck.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import * as path from "@std/path";
import { defineDeck } from "jsr:@bolt-foundry/gambit";
import { z } from "npm:zod";

const POLICY_ROOT = path.resolve(
path.dirname(path.fromFileUrl(import.meta.url)),
"../gambit-bot/policy",
);

export default defineDeck({
label: "policy_read",
contextSchema: z.object({
path: z.string().optional().describe(
"Policy file path under policy/. Defaults to policy/README.md.",
),
}),
responseSchema: z.object({
status: z.number().optional(),
message: z.string().optional(),
payload: z.object({
path: z.string(),
contents: z.string(),
}).optional(),
}),
async run(ctx) {
const requestedPath = (ctx.input.path ?? "policy/README.md").trim();

let resolved;
try {
resolved = await resolvePolicyPath(requestedPath);
} catch (err) {
return {
status: 400,
message: err instanceof Error ? err.message : String(err),
};
}

try {
const stat = await Deno.stat(resolved.fullPath);
if (!stat.isFile) return { status: 409, message: "path is not a file" };
const contents = await Deno.readTextFile(resolved.fullPath);
return {
status: 200,
payload: { path: resolved.relativePath, contents },
};
} catch (err) {
if (err instanceof Deno.errors.NotFound) {
return { status: 404, message: "path not found" };
}
return {
status: 500,
message: err instanceof Error ? err.message : String(err),
};
}
},
});

async function resolvePolicyPath(inputPath: string): Promise<{
fullPath: string;
relativePath: string;
}> {
if (!inputPath) throw new Error("path is required");
const root = await Deno.realPath(POLICY_ROOT);

const normalized = inputPath.startsWith("policy/")
? inputPath.slice("policy/".length)
: inputPath;
const normalizedInput = path.normalize(normalized);
const segments = normalizedInput.split(/\\|\//g);
if (segments.includes("..")) throw new Error("path traversal is not allowed");

const candidate = path.resolve(root, normalizedInput);
const relativePath = path.relative(root, candidate);
if (relativePath.startsWith("..") || path.isAbsolute(relativePath)) {
throw new Error("path escapes policy root");
}
if (!relativePath.endsWith(".md")) {
throw new Error("policy_read only supports .md files under policy/");
}

return {
fullPath: candidate,
relativePath: `policy/${relativePath.replaceAll("\\", "/")}`,
};
}
6 changes: 6 additions & 0 deletions src/decks/actions/policy_read/PROMPT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
+++
label = "policy_read"
execute = "../policy_read.deck.ts"
+++

Compute-only deck for loading policy docs under `policy/`.
43 changes: 43 additions & 0 deletions src/decks/actions/policy_search/PROMPT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
+++
label = "policy_search"
modelParams = { model = "openrouter/openai/gpt-5.1-chat", temperature = 0 }
contextSchema = "../schemas/policy_search_input.zod.ts"
responseSchema = "../schemas/policy_search_output.zod.ts"

[[actions]]
name = "policy_read"
path = "../policy_read/PROMPT.md"
description = "Load a policy doc under policy/."
+++

You are a policy summarizer for Gambit Bot.

Your job: read and summarize the most relevant policies for an upcoming change.

Discovery flow:

1. Call `policy_read` with `path="policy/README.md"` first.
2. Discover candidate policy file paths from the README markdown links.
3. Select the most relevant policy paths for the requested change.
4. Call `policy_read` for each selected policy path.
5. Summarize those loaded policy contents and return the result.

Selection rules:

- Do not invent paths.
- Prioritize the smallest set that still gives safe coverage.
- Prefer 2-3 docs by default unless a broader set is clearly needed.
- Always include `policy/deck-format-1.0.md` when frontmatter, schema, or deck
structure may change.
- If uncertain, include `policy/README.md` first.

Response requirements:

- Return `summaries` as an array of scoped guidance items.
- Each item must include:
- `appliesTo`: the part of the proposal this guidance maps to
- `summary`: concise policy guidance for that part
- Focus each item on how policy applies to the user's proposed change.
- Do not return path lists or per-policy metadata in the response.

![respond](gambit://snippets/respond.md)
13 changes: 13 additions & 0 deletions src/decks/actions/schemas/policy_search_input.zod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { z } from "npm:zod";

export default z.object({
changeSummary: z.string().describe(
"Short summary of the change you are about to make.",
),
userRequest: z.string().describe(
"Original user ask, if available.",
).optional(),
limit: z.number().int().min(1).max(5).default(3).describe(
"Maximum number of policy docs to recommend.",
),
});
14 changes: 14 additions & 0 deletions src/decks/actions/schemas/policy_search_output.zod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { z } from "npm:zod";

export default z.object({
summaries: z.array(z.object({
appliesTo: z.string().describe(
"What part of the proposed change this summary applies to.",
),
summary: z.string().describe(
"Policy guidance for that part of the change.",
),
})).min(1).describe(
"Scoped policy summaries so the parent knows where each policy applies.",
),
});
3 changes: 2 additions & 1 deletion src/decks/gambit-bot/INTENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@

## Constraints

- `PROMPT.md` is the canonical entrypoint; INTENT/POLICY are guidance only.
- `PROMPT.md` is the canonical entrypoint; `INTENT.md` and `policy/*.md` are
guidance only.
- Use existing Gambit runtime and test-bot primitives; do not fork pipelines.
- Avoid introducing remote dependencies without explicit opt-in.

Expand Down
11 changes: 6 additions & 5 deletions src/decks/gambit-bot/PROMPT.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ path = "../actions/bot_list/PROMPT.md"
description = "List files and directories under the bot root."

[[actions]]
name = "internal_knowledge_read"
path = "../actions/internal_knowledge_read/PROMPT.md"
description = "Read files from Gambit Bot's internal knowledge folder. Do this before writing most files, so you know what to do."
name = "policy_search"
path = "../actions/policy_search/PROMPT.md"
description = "Policy gateway: finds relevant policies and returns a summary for the planned change."

[[actions]]
name = "bot_deck_review"
Expand Down Expand Up @@ -147,5 +147,6 @@ On the first substantive user turn in a session, do this startup flow once:
files.
4. If listing fails, say so briefly and continue with cautious assumptions.

When you're unsure about deck format or frontmatter requirements, read
`notes/deck-format-1.0.md` via `internal_knowledge_read` before proposing edits.
When policy details are relevant to a change, or you're unsure about deck
format/frontmatter requirements, call `policy_search` with a short summary of
the planned change and use the returned `summaries` before writing.
8 changes: 4 additions & 4 deletions src/decks/gambit-bot/graders/deck_format_policy_llm/PROMPT.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ You evaluate whether GambitBot followed deck-editing policy for the graded turn.
Pass criteria (all must be true):

1. The assistant proposes or writes Deck Format v1.0 assets (for example
`PROMPT.md`, `INTENT.md`, optional `POLICY.md`) instead of inventing ad-hoc
`.deck.md` custom DSL unless explicitly requested by the user.
`PROMPT.md`, `INTENT.md`, and optional `policy/*.md`) instead of inventing
ad-hoc `.deck.md` custom DSL unless explicitly requested by the user.
2. If the turn is an edit/update flow, the assistant checks existing workspace
files before making broad structural rewrites (for example via `bot_list`,
`bot_read`, or `bot_exists` evidence in the session).
3. When frontmatter/schema details are uncertain, the assistant consults
internal guidance (for example `internal_knowledge_read` of
`notes/deck-format-1.0.md`) before asserting schema shape.
internal guidance (for example `policy_search` with a change summary) before
asserting schema shape.
4. The assistant tone stays concise and practical (no inflated persona flourish
that distracts from the task).

Expand Down
26 changes: 26 additions & 0 deletions src/decks/gambit-bot/policy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Gambit Bot Policy Guide

Use this folder as the policy source of truth for Gambit Bot.

## Quick explainer

- [`core-invariants.md`](./core-invariants.md): Non-negotiable guardrails
(local-first, Deck Format v1.0, bot-root boundaries, canonical `PROMPT.md`
entrypoint).
- [`interaction.md`](./interaction.md): Conversation and workflow behavior
expectations (minimal questions, scenario wording, starter scenario + grader
creation).
- [`safety-reliability.md`](./safety-reliability.md): Safety checks and fallback
behavior when workflows may break or model/runtime setup is invalid.
- [`frontmatter-guardrails.md`](./frontmatter-guardrails.md): Rules and
checklist for safe frontmatter and schema editing.
- [`deck-format-1.0.md`](./deck-format-1.0.md): Full Deck Format v1.0
specification and folder contract.

## Usage

- Root decks should call `policy_search` with a short change summary.
- `policy_search` reads relevant policy docs internally and returns a usable
`summaries` array with scoped guidance.
- Root decks should use those summaries and should not read policy files
directly.
8 changes: 8 additions & 0 deletions src/decks/gambit-bot/policy/core-invariants.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Core Invariants

- Stay local-first: do not introduce remote dependencies without explicit opt-in
and a clear explanation of implications.
- Keep `PROMPT.md` as the canonical deck entrypoint.
- Use [Deck Format v1.0](./deck-format-1.0.md) (TOML frontmatter) with
`[modelParams]` populated.
- Do not write outside the bot root; use the bot file tools.
Loading