p4-plan-search-queries — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited p4-plan-search-queries (Agent Skill) and scored it 96/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 1 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
The search_tasks tool accepts a findQuery parameter for searching using P4 Plan's query language. Use it to filter by status, assignee, priority, dates, or any combination. For simple name search use Itemname:Text("text").
⚠️ Column names must be spelled exactly as shown below. There is no `priority` column — use `Productbacklogpriority`, `Sprintpriority`, or `Bugpriority`. There is no `itemType` — use `Itemtype`. No spaces in column names, exact casing. Values use human-readable strings like "Very high priority", NOT enum values like "veryHigh".
| ❌ Wrong | ✅ Correct |
|---|---|
itemType="Bug" | Itemtype="Bug" |
priority="veryHigh" | Productbacklogpriority="Very high priority" |
Bugpriority="veryHigh" | Bugpriority="Very high priority" |
type="Bug" | Itemtype="Bug" |
assignedTo:Resource("john") | Assignedto:Resource("john") |
itemName:Text("login") | Itemname:Text("login") |
status = "notDone" (spaces around =) | Status="Not done" (no spaces around =) |
Status="notDone" | Status="Not done" (use human-readable text) |
Severity="high" | Severity="A" (A/B/C/D letter codes) |
Severity="Critical" | Severity="A" (use letter codes, not text names) |
| Operator | Meaning | Example |
|---|---|---|
AND or + | Both conditions true | Status="Not done" AND Severity="A" |
OR | Either condition true | Status="Not done" OR Status="In progress" |
NOT or ! | Negate condition | NOT Status="Completed" |
- | First true, second not | Status="Not done" - Workremaining>5 |
() | Group conditions | (Severity="A" OR Severity="B") AND Status="Not done" |
| Value | Meaning |
|---|---|
Not done | Not started |
In progress | Currently active |
Completed | Done |
Blocked | Blocked |
Example: Status="Not done" OR Status="In progress"
There are three separate priority columns — each applies to a different item type. All use the same values:
| Column name | Applies to | Example syntax |
|---|---|---|
Productbacklogpriority | Backlog items | Productbacklogpriority="Very high priority" |
Sprintpriority | Sprint items | Sprintpriority="High priority" |
Bugpriority | Bugs only | Bugpriority="Very high priority" |
Priority values (same for all three columns):
| Value |
|---|
Very high priority |
High priority |
Medium priority |
Low priority |
Very low priority |
Use Itemtype to filter by type. Exact values:
| Value | Description |
|---|---|
Bug | Bug in QA section |
BacklogTask | Task in backlog |
ScheduledTask | Task in planning/Gantt |
Sprint | Sprint |
Release | Release milestone |
Example: Itemtype="Bug" AND Status="Not done"
| Value | Meaning |
|---|---|
A | Critical — system unusable |
B | High — major functionality broken |
C | Medium — partially working |
D | Low — minor / cosmetic |
Example: Severity="A" OR Severity="B"
| Value |
|---|
None |
Low |
Medium |
High |
Example: Confidence="High", Risk="Low"
| Column | Syntax | Notes |
|---|---|---|
Itemname | Itemname:Text("search text") | Item name/description |
Assignedto | Assignedto:Resource("username") | Assigned user |
Points | Points=5 | Story points |
Estimateddays | Estimateddays=3 | Estimated days |
Workremaining | Workremaining>0 | Hours remaining |
Duration | Duration=5 | Duration in days |
Color | Color="red" | Item color |
Committedto | Committedto:Item("Sprint 1") | Committed to sprint |
Level | Level<3 | Hierarchy depth |
P4 Plan items have two different IDs, and both are queryable. Picking the wrong one is the most common cause of a search that returns nothing.
| Column | Syntax | What it is |
|---|---|---|
ID | ID=4217 | The local ID -- the number shown in the P4 Plan UI's "ID" column |
Databaseid | Databaseid=500100 | The database ID -- the id every MCP tool accepts and returns |
Both are returned on every item -- as id (database) and localID (local) -- so you rarely need to look either up. When a user quotes a bare number, decide by where it came from, not by how long it is:
use ID=.
pass it straight to get_tasks.
If the origin is genuinely unclear, ask, or try Databaseid= and fall back to ID= -- do not guess from digit count, since the two ranges overlap.
# User says "look at bug 4217" -- resolve the UI number to a real item
ID=4217
# Confirm which item a database ID refers to, scoped to one section
Databaseid=500100Never binary-search for a local ID. A single ID= query resolves it directly.
An item's localID is a property of the item, not of the query -- it is resolved from the item alone and is the same whichever section you search. projectID is not: for an item listed in two sections (a committed backlog item, say) a search reports the section you searched, while get_tasks reports the section the item was created in. Use id for identity.
Local IDs are unique only within one section. The Backlog, Planning and QA sections of the same project can each have an item with ID=42, so always run an ID= query against the section the user means, and say which section you searched when you report the result.
| Column | Syntax | Notes |
|---|---|---|
Pipelineorworkflow | Pipelineorworkflow="Feature Delivery" | The workflow or pipeline set on the item, by name |
Use the workflow/pipeline name exactly as get_workflows returns it. This matches both status workflows and pipelines — a pipeline match returns the items carrying the pipeline, not the pipeline tasks it spawned. See the pipeline skill for how to list the steps themselves.
| Column | Syntax |
|---|---|
Itemname | Itemname:Text("text") |
Comments | Comments:Text("text") |
Detaileddescription | Detaileddescription:Text("text") |
Stepstoreproduce | Stepstoreproduce:Text("text") |
Userstory | Userstory:Text("text") |
Hyperlink | Hyperlink:Text("text") |
Subprojectpath | Subprojectpath:Text("text") |
Releasetag | Releasetag:Text("text") |
Item names in P4 Plan routinely contain double quotes. Escape them as \" inside the literal -- substituting a name verbatim breaks the parse:
# Item actually named: Checkout redesign ("Buy now" flow)
# CORRECT -- embedded quotes escaped
Subprojectpath:Text("Checkout redesign (\"Buy now\" flow)")
# WRONG -- fails with the misleading error 'Space is not allowed here'
Subprojectpath:Text("Checkout redesign ("Buy now" flow)")The error names whitespace, not quoting, so it is easy to misdiagnose. If a Text() search fails that way, check for unescaped quotes before touching anything else. Searching a distinctive quote-free fragment of the name also works and avoids the problem entirely.
| Column | Syntax |
|---|---|
Start | Start="2026-03-01" |
Finish | Finish<="2026-03-31" |
Lastupdatedon | Lastupdatedon>="2026-03-01" |
Lastcommentedon | Lastcommentedon>="2026-03-01" |
Originallycreatedon | Originallycreatedon>="2026-01-01" |
Comparison operators: =, <, >, <=, >=
Date range: fromdatetodate(2026-01-01, 2026-03-31) or fromdatetodate(now-7d, now)
| Column | Syntax |
|---|---|
Assignedto | Assignedto:Resource("username") |
Originallycreatedby | Originallycreatedby:Resource("username") |
LastUpdatedBy | LastUpdatedBy:Resource("username") |
Editableby | Editableby:Resource("username") |
Watch | Watch:Resource("username") |
x
| Condition | Syntax |
|---|---|
| Hidden items | GeneralconditionHidden=true |
| Assigned to me | GeneralconditionAssignedtome=true |
| Has sub items | GeneralconditionHassubitems=true |
| In current sprint | GeneralconditionIncurrentsprint=true |
| Not assigned | GeneralconditionNotassigned=true |
| User story | GeneralconditionUserstory=true |
| Epic | GeneralconditionEpic=true |
| Ongoing work | GeneralconditionOngoingwork=true |
| Completed (scheduled) | ScheduledconditionCompleted=true |
| In progress (scheduled) | ScheduledconditionInprogress=true |
| Not started yet (scheduled) | ScheduledconditionNotstartedyet=true |
| Overdue (scheduled) | ScheduledconditionOverdue=true |
| Critical path (scheduled) | ScheduledconditionCriticalpath=true |
| Release overdue | GeneralconditionReleaseoverdue=true |
| Keyword | Meaning |
|---|---|
mywork | Items assigned to current user |
assignedtome | Same as mywork |
week15 | Items scheduled during week 15 |
fromdatetodate(a,b) | Date range (supports now-Nd, now+Nd) |
Custom columns use their name directly: MyColumnName:Text("value"), MyColumnName=<number>, etc. The operator depends on the custom column type.
# All open bugs with critical severity
Itemtype="Bug" AND Status="Not done" AND Severity="A"
# High-priority bugs
Itemtype="Bug" AND Bugpriority="Very high priority"
# High-priority backlog items (NOT Bugpriority, NOT priority)
Productbacklogpriority="Very high priority" AND Status="Not done"
# Open bugs with high or critical severity
Itemtype="Bug" AND (Severity="A" OR Severity="B") AND NOT Status="Completed"
# Items assigned to a specific user
Assignedto:Resource("john.doe")
# Unassigned items in current sprint
GeneralconditionIncurrentsprint=true AND GeneralconditionNotassigned=true
# Items updated in the last 7 days
Lastupdatedon>=fromdatetodate(now-7d, now)
# All user stories with points
GeneralconditionUserstory=true AND Points>0
# Overdue scheduled tasks
ScheduledconditionOverdue=true
# Items with specific text in comments
Comments:Text("blocker")
# Blocked items
Status="Blocked"
# In-progress items with high sprint priority
Status="In progress" AND Sprintpriority="High priority"
# Bugs by severity
Itemtype="Bug" AND Severity="A"When search_tasks returns "limit exceeded", the result set is too large for one query. Do NOT retry the same query or ask the user. Instead, decompose the query by splitting it into multiple narrower sub-queries along a known dimension, run them one at a time, and aggregate the results.
| Dimension | Values to iterate | Best when… |
|---|---|---|
| Severity | Severity="A", Severity="B", Severity="C", Severity="D" | User wants bug breakdown by severity |
| Status | Status="Not done", Status="In progress", Status="Completed", Status="Blocked" | User wants status distribution |
| Bugpriority | Bugpriority="Very high priority" … Bugpriority="Very low priority", Bugpriority="None" | User wants priority breakdown |
| Date range | Split into monthly/weekly ranges with fromdatetodate() | User wants trends over time |
AND <dimension>=<value> to the original query for each possible value.count from each result.# User asks: "Show me a pie chart of all bugs by severity"
# Original query that would fail: Itemtype="Bug"
# Decompose by Severity:
Severity="A" → count: 781
Severity="B" → count: 2030
Severity="C" → count: 3069
Severity="D" → count: 1517
# Total: 7397. Render pie chart from these 4 counts.
# User asks: "How many open bugs per priority?"
# Decompose by Bugpriority, all with AND Status="Not done":
Status="Not done" AND Bugpriority="Very high priority" → count: ...
Status="Not done" AND Bugpriority="High priority" → count: ...
Status="Not done" AND Bugpriority="Medium priority" → count: ...
Status="Not done" AND Bugpriority="Low priority" → count: ...
Status="Not done" AND Bugpriority="Very low priority" → count: ...~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.