Skip to content

feat(harness): make the confirm gate's gated-tool set injectable - #1714

Merged
pyramation merged 2 commits into
mainfrom
feat/injectable-gated-tools
Aug 13, 2026
Merged

feat(harness): make the confirm gate's gated-tool set injectable#1714
pyramation merged 2 commits into
mainfrom
feat/injectable-gated-tools

Conversation

@pyramation

@pyramation pyramation commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Summary

createConfirmGate hardcoded MUTATING_DB_TOOLS as the set of tool calls that need a human decision, making the policy a property of the harness rather than of the host. A remote coding host (constructive-planning#1633: pi in a Kubernetes Job against a cloned repo) wants to gate bash/force-push and auto-approve reads instead.

The set is now one more injected dependency in the existing ConfirmGateDeps bag, defaulting to MUTATING_DB_TOOLS:

export type ConfirmGateDeps = {
  isProjectRunnable(cwd: string): Promise<boolean>;
  hasDataToken(cwd: string): Promise<boolean>;
  resolveTemplatePreview(...): Promise<ConfirmPreview | undefined>;
+ /** Defaults to MUTATING_DB_TOOLS. */
+ gatedTools?: ReadonlySet<string>;
};

-if (!MUTATING_DB_TOOLS.has(event.toolName)) return;
+const gatedTools = deps.gatedTools ?? MUTATING_DB_TOOLS;   // resolved once, at construction
+if (!gatedTools.has(event.toolName)) return;

agentic/pi's adapter forwards an optional gatedTools straight through, so a pi-based host can carry its own policy without reaching into the harness.

Shape: ReadonlySet<string>, not a predicate. Every current caller wants a set, a set is data (so a persona can carry it as JSON and it can be diffed/logged/sent over the wire), and it stays interchangeable with the exported default. A predicate is only more expressive for per-argument gating, which no caller needs today — and the gate already special-cases arguments internally (manage_entity_types action: 'list', tokenless add_records/create_api_key), so a predicate would create two competing places to express the same rule. ReadonlySet rather than Set so the default constant can't be mutated by the gate. Widening to a predicate later is a superset of this signature and stays backwards-compatible.

MUTATING_DB_TOOLS stays exported and is still the default, so Desktop/CLI behaviour is unchanged.

Call sites verified unchanged (none pass gatedTools)

  • agentic/pi/src/confirm-gate.ts — only harness consumer of createConfirmGate; now forwards deps.gatedTools (undefined for all existing callers → default).
  • agentic/pi/src/index.ts — wires the real resolvers, no gatedTools.
  • agentic/pi/__tests__/confirm-gate.test.ts — passes.
  • agentic/harness/__tests__/gating.test.ts — all pre-existing cases pass untouched.
  • No other reference to MUTATING_DB_TOOLS, createConfirmGate, or ConfirmGateDeps exists in the monorepo (agentic/cli only imports the harness's skills/dirs helpers; the desktop host is not vendored here — it consumes @agentic-kit/harness via the pi adapter).

Tests

Added to agentic/harness/__tests__/gating.test.ts: the whole default set is still gated when nothing is injected; with gatedTools: new Set(['bash']), bash is gated (a tool the default never gates) and delete_table stops being gated; the decline guard and the headless "no confirmation UI" block apply to injected tools too; an empty set gates nothing.

Incidental unblock: three expired release-age waivers removed

The build job failed on pnpm run policy:check before running any tests, identically on main: the @playwright/test@1.62.1, @types/react@19.2.18 and @types/react-dom@19.2.4 waivers in pnpm-policy.yaml carried until: 2026-08-13, i.e. today. Per the comment block above exceptions:, until is the day the pinned version turns two weeks old — so those waivers are now obsolete, not in need of extension. They are deleted, and pnpm-workspace.yaml's derived minimumReleaseAgeExclude block and header count are regenerated with npx pnpm-policy generate (not hand-edited). The pinned versions now clear the 2-day minimumReleaseAge on their own: policy:check reports pnpm-workspace.yaml matches the policy and pnpm install --frozen-lockfile still resolves. No control was weakened — the exempt set shrank.

Link to Devin session: https://app.devin.ai/sessions/d57afe90832a4d7197b9b41ff4ca4f43
Requested by: @pyramation

@pyramation pyramation self-assigned this Aug 13, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@pyramation
pyramation merged commit db8e0be into main Aug 13, 2026
20 checks passed
@pyramation
pyramation deleted the feat/injectable-gated-tools branch August 13, 2026 19:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant