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
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,9 @@ before mutating managed files.
`instructions init` and `bun run seed` also seed
`global-agent-rules-standard`, the managed global/system prompt source for
session renaming, task-scoped worktrees, PR-first landing, protected-branch
push safety, autonomous repair, Hasna CLI source-of-truth usage, conversation
surface routing, and unbudgeted Codewith goals unless a user asks for budgets.
push safety, no brittle hardcoding when source-of-truth or reusable abstractions
exist, autonomous repair, Hasna CLI source-of-truth usage, conversation surface
routing, and unbudgeted Codewith goals unless a user asks for budgets.

## Machine-aware Profiles

Expand Down
16 changes: 11 additions & 5 deletions docs/managed-prompt-hierarchy.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,20 @@ The managed global prompt must include these rules:
3. PR-first landing: normal changes go through branch/worktree plus PR.
4. Never push directly to `main`, the default branch, or any protected branch
unless the user explicitly instructs that exact repo and operation.
5. Agents act autonomously: diagnose, repair, validate, and iterate on the
5. Agents must not hardcode brittle values, paths, provider names, config,
business logic, environment-specific IDs, or one-off mappings when a
source-of-truth, schema/config-driven, package-owned, reusable, or cleaner
abstraction exists. This is especially strict in medium and large
applications; explicit constants, fixtures, tests, and temporary
compatibility shims are allowed only when scoped, named, and justified.
6. Agents act autonomously: diagnose, repair, validate, and iterate on the
owning CLIs, packages, and workflows before asking the user. Ask only when
blocked by destructive decisions, secret-bearing decisions, user-only
authority, or external state the agent cannot safely obtain.
6. Use Hasna CLIs/packages as source of truth: `todos`, `conversations`,
7. Use Hasna CLIs/packages as source of truth: `todos`, `conversations`,
`mementos`, `knowledge`, `projects`, `repos`, `accounts`, `instructions`,
`machines`, `secrets`, and `access`.
7. Use the fleet conversation surfaces correctly:
8. Use the fleet conversation surfaces correctly:
`announcements` for `[FREEZE]`, `[UNFREEZE]`, `[BREAKING]`, `[CUTOVER]`,
`[POLICY]`, and `[RELEASE]`; `incidents` for outages, crash loops, data risk,
or security exposure; `git-publishing` before and after package publishes;
Expand All @@ -84,10 +90,10 @@ The managed global prompt must include these rules:
discussion; project/product channels for normal work; and `conversations
blockers` for blocker discovery. Do not invent or refer to a literal
blockers channel.
8. Never set Codewith goal/token budgets or goal-plan budgets unless the user
9. Never set Codewith goal/token budgets or goal-plan budgets unless the user
explicitly asks for a budget. Durable goals and goal plans should be
unbudgeted by default.
9. Never expose secrets in prompts, tasks, memories, conversations, manifests,
10. Never expose secrets in prompts, tasks, memories, conversations, manifests,
reports, logs, or PR text. Reference vault item names and grants only.

## Implementation Notes
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hasna/instructions",
"version": "0.4.5",
"version": "0.4.6",
"description": "AI coding agent instruction & configuration manager \u2014 store, version, apply, and share all your AI coding configs. CLI + MCP + HTTP API (instructions-serve) + generated SDK + Dashboard.",
"type": "module",
"main": "dist/index.js",
Expand Down
28 changes: 28 additions & 0 deletions src/lib/global-agent-rules-standard.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { planSessionRender, sourceFromConfig } from "./session-render";
import {
GLOBAL_AGENT_RULES_STANDARD_CONTENT,
GLOBAL_AGENT_RULES_STANDARD_SLUG,
NO_BRITTLE_HARDCODING_RULE,
ensureGlobalAgentRulesStandardConfig,
} from "./global-agent-rules-standard";

Expand Down Expand Up @@ -40,6 +41,9 @@ describe("global agent rules standard", () => {
expect(content).toContain("Never mutate shared checkouts");
expect(content).toContain("PR-first landing");
expect(content).toMatch(/Never push directly to `main`, the default branch, or any protected branch/);
expect(content).toContain(NO_BRITTLE_HARDCODING_RULE);
expect(content).toContain("medium and large applications");
expect(content).toContain("temporary compatibility shims are allowed only when scoped, named, and justified");
expect(content).toContain("Act autonomously");
expect(content).toContain("owning\n CLIs, packages, and workflows");
expect(content).toContain("destructive decisions, secret-bearing decisions, user-only authority");
Expand Down Expand Up @@ -92,9 +96,33 @@ describe("global agent rules standard", () => {
expect(plan.files[0]?.content).toContain("Global Coding Agent Rules Standard");
expect(plan.files[0]?.content).toContain("Never mutate shared checkouts");
expect(plan.files[0]?.content).toContain("conversations blockers");
expect(plan.files[0]?.content).toContain(NO_BRITTLE_HARDCODING_RULE);
expect(plan.manifest.sources[0]?.layer).toBe("global");
});

test("renders the no-hardcoding rule into Codewith and Antigravity plans", async () => {
const config = await ensureGlobalAgentRulesStandardConfig(new LocalConfigStore(db));
const source = sourceFromConfig(config);

const codewith = planSessionRender({
tool: "codewith",
profile: "account999",
targetHome: "/tmp/codewith-account999",
sources: [source],
});
expect(codewith.files[0]?.relativePath).toBe("CODEWITH.md");
expect(codewith.files[0]?.content).toContain(NO_BRITTLE_HARDCODING_RULE);

const antigravity = planSessionRender({
tool: "antigravity",
profile: "account999",
projectRoot: "/tmp/repo",
sources: [source],
});
expect(antigravity.files[0]?.relativePath).toBe(".agents/rules/01-global-agent-rules-standard.md");
expect(antigravity.files[0]?.content).toContain(NO_BRITTLE_HARDCODING_RULE);
});

test("platform profiles link the global rules standard when present", async () => {
const standard = await ensureGlobalAgentRulesStandardConfig(new LocalConfigStore(db));
const profiles = await ensurePlatformProfiles(new LocalConfigStore(db));
Expand Down
11 changes: 7 additions & 4 deletions src/lib/global-agent-rules-standard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { resolveConfigStore, type ConfigStore } from "../data/config-store.js";

export const GLOBAL_AGENT_RULES_STANDARD_SLUG = "global-agent-rules-standard";

export const NO_BRITTLE_HARDCODING_RULE = "Do not hardcode brittle values, paths, provider names, config, business logic, environment-specific IDs, or one-off mappings when a source-of-truth, schema/config-driven, package-owned, reusable, or cleaner abstraction exists. This is especially strict in medium and large applications. Explicit constants, fixtures, tests, and temporary compatibility shims are allowed only when scoped, named, and justified.";

export const GLOBAL_AGENT_RULES_STANDARD_CONTENT = `# Global Coding Agent Rules Standard

This standard is managed global/system prompt source content for Hasna coding
Expand All @@ -27,24 +29,25 @@ policy source supersedes them.
request before landing.
5. Never push directly to \`main\`, the default branch, or any protected branch
unless the user explicitly instructs that exact repo and operation.
6. ${NO_BRITTLE_HARDCODING_RULE}

## Autonomy and Source-of-Truth Tools

6. Act autonomously. Diagnose, repair, validate, and iterate on the owning
7. Act autonomously. Diagnose, repair, validate, and iterate on the owning
CLIs, packages, and workflows before asking the user. Ask only when blocked
by destructive decisions, secret-bearing decisions, user-only authority, or
external state the agent cannot safely obtain.
7. Use Hasna CLIs/packages as the source of truth: \`todos\`, \`conversations\`,
8. Use Hasna CLIs/packages as the source of truth: \`todos\`, \`conversations\`,
\`mementos\`, \`knowledge\`, \`projects\`, \`repos\`, \`accounts\`,
\`instructions\`, \`machines\`, \`secrets\`, and \`access\`.
8. Secrets safety is mandatory. Never expose secrets in prompts, tasks,
9. Secrets safety is mandatory. Never expose secrets in prompts, tasks,
memories, conversations, manifests, reports, logs, PR text, or any other
agent-visible output. Reference vault item names, secret identifiers, and
access grants only; never print credential values.

## Conversation Surfaces

9. Use default conversation surfaces correctly: \`announcements\` for policy,
10. Use default conversation surfaces correctly: \`announcements\` for policy,
freeze, breaking, cutover, and release notices; \`incidents\` for outages,
crash loops, data risk, or security exposure; \`git-publishing\` before and
after package publishes; \`git-prs\`, \`git-commits\`, and \`git-releases\`
Expand Down
34 changes: 34 additions & 0 deletions src/lib/session-render.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
sourcesFromIdentityExport,
type SessionInstructionSource,
} from "./session-render";
import { GLOBAL_AGENT_RULES_STANDARD_CONTENT, NO_BRITTLE_HARDCODING_RULE } from "./global-agent-rules-standard";

const globalIdentity: SessionInstructionSource = {
id: "global-codewith",
Expand All @@ -30,6 +31,14 @@ const agentIdentity: SessionInstructionSource = {
content: "Prefer repository-local evidence and focused tests.",
};

const globalRulesStandard: SessionInstructionSource = {
id: "global-agent-rules-standard",
label: "Global Agent Rules Standard",
layer: "global",
order: 0,
content: GLOBAL_AGENT_RULES_STANDARD_CONTENT,
};

function hash(content: string): string {
return createHash("sha256").update(content).digest("hex");
}
Expand Down Expand Up @@ -197,6 +206,19 @@ describe("session render planner", () => {
expect(plan.files[0]?.content).toContain("Global Codewith Identity");
});

test("renders the no-hardcoding global rule into Antigravity project rules", () => {
const projectRoot = join(tmpRoot, "repo");
const plan = planSessionRender({
tool: "antigravity",
profile: "account999",
projectRoot,
sources: [globalRulesStandard],
});

expect(plan.files[0]?.relativePath).toBe(".agents/rules/01-global-agent-rules-standard.md");
expect(plan.files[0]?.content).toContain(NO_BRITTLE_HARDCODING_RULE);
});

test("blocks Antigravity planning until a repository root is explicit", () => {
const plan = planSessionRender({
tool: "antigravity",
Expand Down Expand Up @@ -321,6 +343,18 @@ describe("session render planner", () => {
]);
});

test("renders the no-hardcoding global rule into flattened Codewith instructions", () => {
const plan = planSessionRender({
tool: "codewith",
profile: "account999",
targetHome: "/tmp/codewith-account999",
sources: [globalRulesStandard],
});

expect(plan.files[0]?.relativePath).toBe("CODEWITH.md");
expect(plan.files[0]?.content).toContain(NO_BRITTLE_HARDCODING_RULE);
});

test("plans Codewith native imports only when the runtime gate is enabled", () => {
process.env[CODEWITH_NATIVE_IMPORTS_ENV] = "1";
const plan = planSessionRender({
Expand Down
Loading