Skip to content

fix(persona-kit): preserve trigger paths#276

Merged
khaliqgant merged 4 commits into
mainfrom
fix/issue-275-trigger-paths
Jul 16, 2026
Merged

fix(persona-kit): preserve trigger paths#276
khaliqgant merged 4 commits into
mainfrom
fix/issue-275-trigger-paths

Conversation

@miyaontherelay

Copy link
Copy Markdown
Contributor

Closes #275.

Summary

  • preserve valid trigger paths through parseIntegrationTrigger, agent compilation, and persistence
  • expose paths?: string[] on both runtime and authoring trigger interfaces
  • validate configured paths as a non-empty array of non-empty absolute Relayfile globs
  • keep the generated agent schema aligned with parser validation
  • prove GitHub repository and Slack channel scopes survive the exact source-to-upload projection

Failure-class regression

The deploy persistence regression was added and run before the implementation. On pristine origin/main plus the test-only diff:

pnpm --filter @agentworkforce/deploy test
tests 226 | pass 225 | fail 1

Expected GitHub and Slack triggers to retain paths.
Actual triggers retained only `on`.

With this change, the same test is green and the persisted agent block contains both:

/github/repos/AgentWorkforce/workforce/pulls/**
/slack/channels/C_REVIEW/**

Validation

  • pnpm --filter @agentworkforce/persona-kit test — 290/290 passed
  • pnpm --filter @agentworkforce/deploy test — 226/226 passed
  • node --test packages/runtime/dist/define-agent.test.js — 6/6 passed
  • persona-kit, runtime, and deploy package typechecks — passed
  • persona-kit, runtime, and deploy package builds — passed
  • git diff --check — passed

The full runtime suite compiles, but this local host uses Node 25.8.1; 13 existing local-preview permission-boundary tests require the repository's patched Node >=26.3.1. The changed runtime authoring test is run directly and passes.

Compatibility / scope

Triggers without paths are unchanged. Invalid declared paths previously vanished silently and now fail with a precise field path. Unknown trigger-key rejection is intentionally not included: once paths is supported, that temporary mitigation is no longer needed, and rejecting extension keys would conflict with the repository's established forward-compatible parsing policy.

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f1cedb58-191e-415c-a538-1a60dd7d3f05

📥 Commits

Reviewing files that changed from the base of the PR and between 7f4087b and 2a3902e.

📒 Files selected for processing (10)
  • packages/deploy/src/compile-agent.test.ts
  • packages/persona-kit/schemas/agent.schema.json
  • packages/persona-kit/scripts/emit-schema.mjs
  • packages/persona-kit/src/define.test.ts
  • packages/persona-kit/src/define.ts
  • packages/persona-kit/src/emit-schema.test.ts
  • packages/persona-kit/src/parse.test.ts
  • packages/persona-kit/src/parse.ts
  • packages/persona-kit/src/types.ts
  • packages/runtime/src/define-agent.test.ts

📝 Walkthrough

Walkthrough

Trigger definitions now support optional path scopes. Parsing and schema validation enforce non-empty absolute paths, while runtime and deployment tests verify those scopes remain available through agent definition and persistence projection.

Changes

Trigger path scoping

Layer / File(s) Summary
Trigger contracts and parsing
packages/persona-kit/src/define.ts, packages/persona-kit/src/types.ts, packages/persona-kit/src/parse.ts, packages/persona-kit/src/*test.ts
Trigger types expose paths, shared parsing validates absolute path lists, and parsed integration triggers and watch rules preserve them.
Schema path constraints
packages/persona-kit/scripts/emit-schema.mjs, packages/persona-kit/schemas/agent.schema.json, packages/persona-kit/src/emit-schema.test.ts
Generated and checked schemas enforce non-empty path arrays, non-empty items, and leading-slash patterns.
Runtime and persistence propagation
packages/runtime/src/define-agent.test.ts, packages/deploy/src/compile-agent.test.ts
Runtime definitions and compiled-agent persistence projections are tested with GitHub and Slack path scopes.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PersonaDefinition
  participant parseIntegrationTrigger
  participant CompiledAgent
  participant PersistenceProjection
  PersonaDefinition->>parseIntegrationTrigger: provide trigger paths
  parseIntegrationTrigger->>CompiledAgent: return validated paths
  CompiledAgent->>PersistenceProjection: pass compiled triggers
  PersistenceProjection-->>CompiledAgent: retain trigger paths
Loading

Possibly related PRs

  • AgentWorkforce/workforce#267: Introduced the single-file agent compilation and persistence projection pipeline covered by the new preservation test.

Suggested reviewers: khaliqgant

Poem

I’m a rabbit with paths in my paws,
Through schema and parsers, I hop without flaws.
GitHub and Slack keep their scopes nice and bright,
Persistence carries them safely at night.
Boing! No lost paths in sight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: preserving trigger paths in persona-kit.
Description check ✅ Passed The description is directly about preserving and validating trigger paths, matching the changeset.
Linked Issues check ✅ Passed The changes address #275 by parsing, typing, validating, and persisting trigger paths end to end.
Out of Scope Changes check ✅ Passed The modified tests, schema updates, and emit logic all support the trigger-path preservation scope.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-275-trigger-paths

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 10 files

You’re at about 98% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/persona-kit/src/parse.ts Outdated

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a paths property to PersonaIntegrationTrigger to enable scoping wake routing for agents. This change includes updates to the JSON schema, TypeScript interfaces, parser validation, and corresponding tests. A review comment correctly identifies an inconsistency in the path validation logic regarding whitespace handling and provides a robust implementation suggestion that should be addressed.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread packages/persona-kit/src/parse.ts Outdated
Comment on lines +571 to +577
if (typeof path !== 'string' || !path.trim()) {
throw new Error(`${pathContext} must be a non-empty string`);
}
if (!path.startsWith('/')) {
throw new Error(`${pathContext} must start with /`);
}
return path;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current validation logic for paths has an inconsistency. It uses path.trim() to check for emptiness but then uses the original path for the startsWith('/') check and for the return value. This can lead to unexpected behavior with whitespace. For example, a path like " /foo" would be incorrectly rejected, and a path like "/foo " would be accepted and stored with trailing whitespace.

It would be more robust to trim the path once and use the trimmed value for all subsequent validation and for the return value. This is also more consistent with how other values are parsed in this file.

      if (typeof path !== 'string') {
        throw new Error(`${pathContext} must be a string`);
      }
      const trimmed = path.trim();
      if (!trimmed) {
        throw new Error(`${pathContext} must be a non-empty string`);
      }
      if (!trimmed.startsWith('/')) {
        throw new Error(`${pathContext} must start with /`);
      }
      return trimmed;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in da2b05a. The shared path parser now rejects leading or trailing whitespace instead of silently normalizing an exact routing glob, and the generated schema enforces the same contract. Trigger and watch regressions cover padded paths; persona-kit 290/290 and deploy 226/226 pass.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 5 files (changes from recent commits).

You’re at about 98% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/persona-kit/src/parse.ts">

<violation number="1" location="packages/persona-kit/src/parse.ts:556">
P2: Agent schema validation still accepts padded `watch[].paths`, but `parseWatch` now rejects them through this helper. Apply the same non-empty, absolute, no-trailing-whitespace constraints to `WatchRule.paths` in the schema emitter so authoring validation matches parsing.</violation>
</file>

<file name="packages/persona-kit/schemas/agent.schema.json">

<violation number="1" location="packages/persona-kit/schemas/agent.schema.json:57">
P2: Schema consumers can accept trigger paths that `parseIntegrationTrigger` then rejects: `'/ github/**'` matches this pattern because only trailing whitespace is excluded. Constrain the first character after `/` as well, and update `scripts/emit-schema.mjs` with the same expression so regeneration preserves the contract.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

if (typeof path !== 'string' || !path.trim()) {
throw new Error(`${pathContext} must be a non-empty string`);
}
if (path !== path.trim()) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Agent schema validation still accepts padded watch[].paths, but parseWatch now rejects them through this helper. Apply the same non-empty, absolute, no-trailing-whitespace constraints to WatchRule.paths in the schema emitter so authoring validation matches parsing.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/persona-kit/src/parse.ts, line 556:

<comment>Agent schema validation still accepts padded `watch[].paths`, but `parseWatch` now rejects them through this helper. Apply the same non-empty, absolute, no-trailing-whitespace constraints to `WatchRule.paths` in the schema emitter so authoring validation matches parsing.</comment>

<file context>
@@ -553,6 +553,9 @@ function parseAbsolutePathList(value: unknown, context: string): string[] {
     if (typeof path !== 'string' || !path.trim()) {
       throw new Error(`${pathContext} must be a non-empty string`);
     }
+    if (path !== path.trim()) {
+      throw new Error(`${pathContext} must not have leading or trailing whitespace`);
+    }
</file context>

"items": {
"type": "string",
"minLength": 1,
"pattern": "^/(?:.*\\S)?$"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Schema consumers can accept trigger paths that parseIntegrationTrigger then rejects: '/ github/**' matches this pattern because only trailing whitespace is excluded. Constrain the first character after / as well, and update scripts/emit-schema.mjs with the same expression so regeneration preserves the contract.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/persona-kit/schemas/agent.schema.json, line 57:

<comment>Schema consumers can accept trigger paths that `parseIntegrationTrigger` then rejects: `'/ github/**'` matches this pattern because only trailing whitespace is excluded. Constrain the first character after `/` as well, and update `scripts/emit-schema.mjs` with the same expression so regeneration preserves the contract.</comment>

<file context>
@@ -54,7 +54,7 @@
             "type": "string",
             "minLength": 1,
-            "pattern": "^/"
+            "pattern": "^/(?:.*\\S)?$"
           },
           "description": "Optional Relayfile path globs used to scope wake routing before an agent is provisioned. Paths are absolute (leading `/`) and provider-specific.",
</file context>

Comment thread packages/persona-kit/scripts/emit-schema.mjs Outdated
@khaliqgant
khaliqgant merged commit 72f84b7 into main Jul 16, 2026
3 checks passed
@khaliqgant
khaliqgant deleted the fix/issue-275-trigger-paths branch July 16, 2026 19:41
khaliqgant added a commit that referenced this pull request Jul 17, 2026
* feat(review-kit): strip the model's preamble from the review

defineReviewAgent posted `run.output.trim()` — the harness's raw stdout,
which carries whatever the model said on its way to an answer: "Evidence
confirmed.", "I have all evidence needed.", a draft findings list,
"Writing the review now." Every kit-built reviewer publishes the search
alongside the findings.

This cannot be fixed in a charter, and #281's trap list did not include it
because I only learned it after filing. Measured on a real reviewer against
a 150-word cap: 537 words, then 256 once the ban was sharpened, then 191,
then 187 after moving the entire output contract to the top of the charter
where it is read first. Four rewrites of increasing severity bought 4 words.
Stripping in code took the same reviewer to 153 on the next run.

The reason is simple in hindsight: you cannot instruct a model not to
think, only decline to publish the thinking. So the charter owns the SHAPE
— which it gets right reliably, every review nailed the finding format —
and the kit owns the BOUNDARY. Same reason agents/review strips its own
trailing READY sentinel instead of trusting the model to omit it.

reviewBody() cuts at the LAST verdict line: a model that drafts before
writing emits two, and the real review is the final one. No verdict line
means the model ignored the format — pass it through rather than post
nothing, since for an advisory agent silence reads as approval.

Exported so a lens with a bespoke handler gets the same boundary.

Tests cover the three cases: preamble stripped, last-verdict-wins, and
passthrough/empty.

Note: `pnpm --filter @agentworkforce/review-kit typecheck` and test
`traps 2-4` already fail on pristine origin/main in a local checkout —
both assert trigger `paths`, which needs persona-kit >= 4.1.26 (#276).
Unrelated to this change; verified by stashing it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* review-kit: tolerate how the model bolds the verdict line

From @gemini-code-assist's review: /^\*\*Verdict:/ assumed one exact
format. Verified the concern is real — the old pattern misses three
variants models actually produce:

  **Verdict**: Blocker     (colon outside the bold)  -> MISS
  **Verdict** : Blocker    (space before colon)      -> MISS
  **verdict:** Blocker     (casing)                  -> MISS
  **Verdict: Blocker.**    (what production emits)   -> match

A miss is not a graceful degradation here: an unmatched line publishes the
entire preamble, which is the one thing this function exists to prevent.
Being lax costs nothing; being strict costs the whole feature on a
formatting wobble. The charter asks for one form and the production
reviewer emits it — but the point of stripping in code was precisely that
the model's output is not something you can legislate.

The ^ anchor still does the real work: a verdict mentioned mid-sentence in
prose is not matched. Covered by a test.

Tests for all five variants plus the prose case, per the review.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Ricky Schema Cascade <ricky@agent-relay.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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.

persona-kit silently drops trigger paths: trigger scoping is unreachable (agents/review's Slack scope is inert)

2 participants