diff --git a/docs/loops/_example-instance.md b/docs/loops/_example-instance.md
new file mode 100644
index 0000000..97c5670
--- /dev/null
+++ b/docs/loops/_example-instance.md
@@ -0,0 +1,24 @@
+# Loop prompt — <
>
+
+Scaffold + how-to: [loop-template.md](loop-template.md). To run: copy the fenced block below,
+paste after `/loop` (no interval → self-paced, one ticket build→merge per iteration).
+
+Copy this file to `docs/loops/.md`, fill it in, and add a row to the template's Registry.
+
+## At a glance
+
+- **Scope:** <>
+- **Decisions:** <>
+- **Process order:** <>
+
+## Status
+
+| Ticket | Branch / PR | Merge SHA | Jira | Notes |
+| ----------- | ----------- | --------- | ---- | ----- |
+| <> | — | — | — | — |
+
+## Prompt
+
+```
+<>
+```
diff --git a/docs/loops/loop-template.md b/docs/loops/loop-template.md
new file mode 100644
index 0000000..8f9ffe6
--- /dev/null
+++ b/docs/loops/loop-template.md
@@ -0,0 +1,108 @@
+# Loop runner — template & registry
+
+The reusable scaffold for driving a batch of PDX tickets from build → test → validate → merge,
+one ticket per self-paced `/loop` iteration, on top of this repo's documented dev workflow
+(`.claude/commands/git-workflow.md` + `CLAUDE.md`).
+
+- **This file** = the generic template + how-to + the registry of run prompts.
+- **One file per loop prompt** lives beside it in `docs/loops/` (see [Registry](#registry)).
+ Each instance doc is self-contained and ready to paste after `/loop`.
+
+> Internal working docs — NOT customer-facing. PDX ticket IDs are fine in `docs/loops/`
+> (unlike `docs/mcp.md`, `docs/mcp-pilot-guide.md`, `README.md`, the university course).
+
+## Authoring a new loop prompt
+
+1. Copy `docs/loops/_example-instance.md` (or any existing instance) to a new file in `docs/loops/`.
+ Name it for the batch, e.g. `pdx-NNN-MMM.md` or a short descriptive slug.
+2. Query the Jira keys first (`searchJiraIssuesUsingJql` on the keys) to read `issuelinks`
+ and derive a **blocker-safe PROCESS ORDER** before writing the order line.
+3. Fill the `<<…>>` slots in the template below, paste the result into the new instance doc,
+ and record the decisions + status there.
+4. Add a row to the [Registry](#registry).
+5. To run it: open the instance doc, copy its fenced prompt, paste after `/loop`
+ (no interval → self-paced; each iteration takes one ticket build→merge).
+
+## Decisions to make before filling the template
+
+| Decision | Options | Where it lands |
+| --------------------- | ---------------------------------------------------------------- | --------------------- |
+| Autonomy | pause-before-merge / fully-autonomous-to-merge / stop-after-PR | step 9 + EXCEPTIONS |
+| Merge style | merge commit (`--merge`) / squash (`--squash`) | step 9 |
+| Ordering | respect `Blocks`/`is blocked by` links; front-load High severity | PROCESS ORDER |
+| Per-ticket specifics | enum sources, dup decisions, error codes, etc. | TICKET-SPECIFIC RULES |
+| Authoritative sources | confirm before editing — never guess at enums/contracts | TICKET-SPECIFIC RULES |
+
+## Template
+
+```
+Drive <> to merged, one ticket per iteration,
+<>.
+
+AUTHORITATIVE WORKFLOW: follow .claude/commands/git-workflow.md and
+CLAUDE.md exactly (worktree-per-branch, yarn install in
+the worktree, commit format, the compile/test/smoke/lint gate, PR template,
+Copilot handling) — WITH these overrides for this loop:
+ - <>
+ - Merge style: < --merge --delete-branch`
+ OR squash `--squash`>>.
+ - Jira cloudId: 3c8a4f06-8ecc-4723-876f-b096b816c6ec.
+
+PROCESS ORDER (respects blockers): <>.
+ - <>
+ - <>
+
+EACH ITERATION:
+1. Pick the FIRST ticket in PROCESS ORDER not yet Closed whose blocker is Closed.
+ If all are Closed (or closed-as-duplicate), STOP THE LOOP — don't reschedule.
+ Print a final summary: ticket → PR → merge SHA → Jira status.
+2. getJiraIssue, re-read description/acceptance criteria. Transition to In
+ Progress, assign to me.
+3. Create branch + worktree off develop; run `yarn install` in the worktree.
+4. Implement per acceptance criteria + this repo's CLAUDE.md: unit tests in
+ test/unit/mcp/Tools.test.ts, docs/mcp.md for any new error code /
+ changed tool / enum, smoke entry + TOTAL_EXPECTED bump in
+ scripts/mcp-smoke.cjs when a tool/validator is added, NO internal PDX ticket
+ IDs in customer-facing docs.
+5. Gate before commit (in the worktree) — all four MUST pass, fix and re-run,
+ never --no-verify:
+ yarn compile
+ node_modules/.bin/nyc node_modules/.bin/mocha "test/**/*.test.ts"
+ node scripts/mcp-smoke.cjs 2>/dev/null
+ yarn lint
+6. Stage explicitly (never git add -A). Commit format:
+ : ():
+
+ RCA: <≥40 chars>
+ Fix: <≥40 chars>
+7. Push (pre-push runs build+test). Open PR to develop using the workflow's PR
+ template, including the Jira link.
+8. Wait for CI + Copilot. Poll `gh pr checks ` and `gh pr view --comments`.
+ Failing check OR valid Copilot/security comment → fix in the same worktree,
+ commit, push, re-poll. Re-poll patiently; CI + Copilot take minutes.
+9. < --merge --delete-branch`.">>
+10. Transition Jira to Closed. Remove the worktree (git worktree remove …; prune).
+11. End the iteration; self-pace to the next ticket.
+
+TICKET-SPECIFIC RULES:
+ <>
+
+EXCEPTIONS — STOP the loop and ask me (do NOT force or guess) if:
+ - <>
+ - CI or the pre-push/commit-msg hooks fail in a way you can't resolve after 3
+ genuine fix attempts.
+ - A Copilot SECURITY comment can't be confidently resolved.
+ - Acceptance criteria are ambiguous enough that you'd be guessing at behaviour.
+ - Any merge conflict against develop that isn't a trivial auto-resolve.
+In every stop case, leave the branch/PR in place, report where you stopped and
+why, and wait for me.
+```
+
+## Registry
+
+| Loop prompt | Scope | Status |
+| -------------------------------- | ---------------------------------------------------------------------------- | ----------- |
+| [pdx-501-505.md](pdx-501-505.md) | MCP dogfooding fixes (comparisonType enum + sf invocation), 2026-05-29 batch | Not started |
diff --git a/docs/loops/pdx-501-505.md b/docs/loops/pdx-501-505.md
new file mode 100644
index 0000000..d12aabf
--- /dev/null
+++ b/docs/loops/pdx-501-505.md
@@ -0,0 +1,137 @@
+# Loop prompt — PDX-501 → PDX-505 (MCP dogfooding fixes)
+
+Scaffold + how-to: [loop-template.md](loop-template.md). To run: copy the fenced block below,
+paste after `/loop` (no interval → self-paced, one ticket build→merge per iteration).
+
+## At a glance
+
+- **Scope:** MCP dogfooding findings from 2026-05-29 (Provar 307) — comparisonType enum
+ correctness/validation (501, 502) and sf invocation/plugin robustness (503, 504, 505).
+- **Decisions:** fully autonomous to merge · merge-commit style (`--merge`) · enum sets
+ confirmed from authoritative Provar-Automation testcases · PDX-503 investigate-then-auto-decide
+ vs PDX-504.
+- **Process order:** PDX-501 → PDX-502 → PDX-504 → PDX-503 → PDX-505.
+
+## Status
+
+| Ticket | Branch / PR | Merge SHA | Jira | Notes |
+| ------- | ----------- | --------- | ----- | -------------------------------------------------------- |
+| PDX-501 | — | — | Draft | comparisonType docs/rules — corrected basis (see prompt) |
+| PDX-502 | — | — | To Do | context-aware validator; blocked by 501 |
+| PDX-504 | — | — | Draft | sf spaces-in-path (High) |
+| PDX-503 | — | — | Draft | reproduce; may close as dup of 504 |
+| PDX-505 | — | — | To Do | PROVAR_PLUGIN_NOT_FOUND |
+
+## Prompt
+
+```
+Drive PDX-501 → PDX-505 to merged, one ticket per iteration, fully autonomously.
+
+AUTHORITATIVE WORKFLOW: follow .claude/commands/git-workflow.md and
+CLAUDE.md exactly (worktree-per-branch, yarn install in
+the worktree, commit format, the compile/test/smoke/lint gate, PR template,
+Copilot handling) — WITH these overrides for this loop:
+ - Run unattended: do NOT stop at the workflow's CONFIRM points. Only the
+ EXCEPTIONS at the bottom may stop the loop.
+ - Merge style: MERGE COMMIT, not squash: `gh pr merge --merge --delete-branch`.
+ - Jira cloudId: 3c8a4f06-8ecc-4723-876f-b096b816c6ec.
+
+PROCESS ORDER (respects blockers): PDX-501, PDX-502, PDX-504, PDX-503, PDX-505.
+ - PDX-501 BLOCKS PDX-502 — don't start 502 until 501 is merged AND Closed.
+ - PDX-503 may be a DUP of PDX-504 — don't start 503 until 504 is merged.
+
+EACH ITERATION:
+1. Pick the FIRST ticket in PROCESS ORDER not yet Closed whose blocker is Closed.
+ If all are Closed (or closed-as-duplicate), STOP THE LOOP — don't reschedule.
+ Print a final summary: ticket → PR → merge SHA → Jira status.
+2. getJiraIssue, re-read description/acceptance criteria. Transition to In
+ Progress, assign to me.
+3. Create branch + worktree off develop; run `yarn install` in the worktree.
+4. Implement per acceptance criteria + this repo's CLAUDE.md: unit tests in
+ test/unit/mcp/Tools.test.ts, docs/mcp.md for any new error code /
+ changed tool / enum, smoke entry + TOTAL_EXPECTED bump in
+ scripts/mcp-smoke.cjs when a tool/validator is added, NO internal PDX ticket
+ IDs in customer-facing docs.
+5. Gate before commit (in the worktree) — all four MUST pass, fix and re-run,
+ never --no-verify:
+ yarn compile
+ node_modules/.bin/nyc node_modules/.bin/mocha "test/**/*.test.ts"
+ node scripts/mcp-smoke.cjs 2>/dev/null
+ yarn lint
+6. Stage explicitly (never git add -A). Commit format:
+ : ():
+
+ RCA: <≥40 chars>
+ Fix: <≥40 chars>
+7. Push (pre-push runs build+test). Open PR to develop using the workflow's PR
+ template, including the Jira link.
+8. Wait for CI + Copilot. Poll `gh pr checks ` and `gh pr view --comments`.
+ Failing check OR valid Copilot/security comment → fix in the same worktree,
+ commit, push, re-poll. Re-poll patiently; CI + Copilot take minutes.
+9. CI green AND no unresolved blocking Copilot/security comments → merge with
+ `gh pr merge --merge --delete-branch`.
+10. Transition Jira to Closed. Remove the worktree (git worktree remove …; prune).
+11. End the iteration; self-pace to the next ticket.
+
+TICKET-SPECIFIC RULES:
+
+ PDX-501 — CORRECTED BASIS. The ticket's "NotEqualTo is invalid, remove it"
+ premise is a STEP-CONTEXT conflation; do NOT delete enum values. There is ONE
+ com.provar.core.model.base.java.ComparisonType enum, and each step type is
+ constrained to a SUBSET. Both subsets are confirmed from Provar-Automation-
+ authored testcases:
+ • AssertValues / assertValuesComparison (apiId
+ com.provar.plugins.bundled.apis.AssertValues) — valid set from
+ C:\Users\mrdai\git\provar-manager-regression\tests\ComparisonTypes.testcase:
+ EqualTo, NotEqualTo, GreaterThan, GreaterThanOrEqualTo, LessThan,
+ LessThanOrEqualTo, IsPresent, IsEmpty, Matches, NotMatches, Contains,
+ NotContains, StartsWith, NotStartsWith, EndsWith, NotEndsWith (16)
+ • UiAssert / uiAttributeAssertion — valid set from the comparisonType
+ attributes in C:\Users\mrdai\git\provar-manager-regression\tests\SauceDemo Purchase Flow (Demo).testcase:
+ EqualTo, Contains, StartsWith, EndsWith, Matches, None (6)
+ (dropdown labels: Equals→EqualTo, Contains, Starts With→StartsWith,
+ Ends With→EndsWith, Matches; Ignore/Read→None. Verify the Ignore-vs-Read
+ distinction against the schema before documenting it as one value.)
+ Fix = make docs/PROVAR_TEST_STEP_REFERENCE.md, the two rule strings in
+ src/mcp/rules/provar_best_practices_rules.json (ASSERT-COMPARISON-001,
+ ASSERT-VALUES-COMPARISON-001), and docs/mcp.md SCOPE each set to its step
+ type and match the authoritative constants exactly. Add the Section-6
+ schema-aware notes from the ticket (encrypted→null via SOQL; rich-text/
+ textarea wrapped in …
, use Contains; Contains direction =
+ expectedValue contains actualValue). Post a Jira comment on PDX-501 recording
+ the corrected basis (two step-scoped subsets; NotEqualTo valid in AssertValues,
+ invalid in UI Assert — the real root cause of the dogfooding failure).
+
+ PDX-502 — reuse the SAME authoritative subsets from 501 (one source of truth;
+ do not hand-duplicate). Validator emits ERROR tier and is CONTEXT-AWARE:
+ AssertValues steps validate against the 16-set; UiAssert/uiAttributeAssertion
+ validate against the 6-set. Tests: every valid AssertValues constant (incl.
+ NotEqualTo) PASSES on an AssertValues step; every valid UI constant PASSES on a
+ UI step; NotEqualTo on a UI step fires ERROR; a constant in NEITHER set (e.g.
+ "NotEqualToo") fires ERROR. Do NOT use NotEqualTo as a context-free negative
+ case. Add rule entry, smoke entry + TOTAL_EXPECTED bump, new error code in
+ docs/mcp.md.
+
+ PDX-504 — make sf invocation space-safe for BOTH executable and args on win32
+ (incl. auto-resolved Program Files paths, not gated on user-supplied sf_path).
+ Unit tests via setSfPlatformForTesting per the acceptance criteria.
+
+ PDX-503 — first reproduce in the MCP context, capturing exitCode/stdout/stderr.
+ If it ONLY repros with spaced sf paths → close as DUPLICATE of PDX-504 (comment
+ the evidence, transition to Closed/Duplicate, no code, no PR). Otherwise
+ implement the hardening fix with a unit test. Record the decision + evidence as
+ a Jira comment either way.
+
+ PDX-505 — add a provar-topic probe + PROVAR_PLUGIN_NOT_FOUND error code
+ mirroring SfNotFoundError with remediation text (sf plugins install
+ @provartesting/provardx-cli); unit test + docs/mcp.md Error Codes entry.
+
+EXCEPTIONS — STOP the loop and ask me (do NOT force or guess) if:
+ - CI or the pre-push/commit-msg hooks fail in a way you can't resolve after 3
+ genuine fix attempts.
+ - A Copilot SECURITY comment can't be confidently resolved.
+ - Acceptance criteria are ambiguous enough that you'd be guessing at behaviour.
+ - Any merge conflict against develop that isn't a trivial auto-resolve.
+In every stop case, leave the branch/PR in place, report where you stopped and
+why, and wait for me.
+```