Skip to content

fix(agentic-engineering): expose the guarded classifier path - #205

Merged
devantler merged 2 commits into
mainfrom
codex/surveyor-classifier-resolution-200
Sep 6, 2026
Merged

fix(agentic-engineering): expose the guarded classifier path#205
devantler merged 2 commits into
mainfrom
codex/surveyor-classifier-resolution-200

Conversation

@devantler

Copy link
Copy Markdown
Contributor

🤖 Generated by the Agentic Engineer

The portfolio surveyor could not locate the only CI classifier its read-only guard permits, leaving default-branch health unknown even when the helper was installed. The guard now returns its own classifier path as JSON in a denied discovery response; the surveyor can use that literal path for the existing guarded call.

Plugin 5.0.1 documents the bounded discovery flow and preserves denial of arbitrary executables, offline input, and malformed calls. A relocated-installation test exercises discovery through the adapter, admission through the guard, and the real classifier's named workflow result.

Fixes #200

@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

@coderabbitai full review

Please review the current full head d0662db for correctness, security, and maintainability. Review the JSON path hint, retained denial, literal path quoting, relocated installation tests, and bounded surveyor recovery.

Local verification: 393 guard cases, 35 adapter cases, 18 classifier cases, and 202 manifest cases passed; ShellCheck, desired-state digests, plugin version gate, and bundled-skill edit guard passed. Independent local correctness/security review found no actionable defects.

User evaluation: the denied probe returned one trusted path; its remote-mode call passed the same guard and the real classifier returned green for agent-plugins main bb19a27. No installed runtime cache was modified.

@coderabbitai

coderabbitai Bot commented Sep 6, 2026

Copy link
Copy Markdown

@devantler I will perform a full review of the current head. I will inspect the specified guard, adapter, classifier, and surveyor flows.

✅ Action performed

Full review finished.

@coderabbitai

coderabbitai Bot commented Sep 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

Next included review available in 48 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Team

Run ID: fa7c476e-2d11-4057-8701-36087b11f77c

📥 Commits

Reviewing files that changed from the base of the PR and between d0662db and 966c942.

📒 Files selected for processing (2)
  • plugins/agentic-engineering/resources/provider-neutral.desired-state.json
  • plugins/agentic-engineering/scripts/forge-readonly-guard.sh

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Team

Run ID: b267dfbc-f186-4e7c-ab8a-b38bbe642e86

📥 Commits

Reviewing files that changed from the base of the PR and between bb19a27 and d0662db.

📒 Files selected for processing (11)
  • .claude-plugin/marketplace.json
  • .github/plugin/marketplace.json
  • plugins/agentic-engineering/.claude-plugin/plugin.json
  • plugins/agentic-engineering/README.md
  • plugins/agentic-engineering/agents/portfolio-surveyor.agent.md
  • plugins/agentic-engineering/plugin.json
  • plugins/agentic-engineering/resources/provider-neutral.desired-state.json
  • plugins/agentic-engineering/scripts/forge-readonly-guard.sh
  • plugins/agentic-engineering/scripts/surveyor-forge-readonly.test.sh
  • scripts/validate-manifests.sh
  • scripts/validate-manifests.test.sh

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Recent review details
⏰ Context from checks skipped due to timeout. (22)
  • GitHub Check: Validate spec (plugins/gitops-kubernetes/skills/gitops-knowledge)
  • GitHub Check: Validate spec (plugins/vibe-coding/skills/needs-stack-mapping)
  • GitHub Check: Validate spec (plugins/engineering-practices/skills/test-driven-development)
  • GitHub Check: Validate spec (plugins/vibe-coding/skills/allowed-stack-guardrail)
  • GitHub Check: Validate spec (plugins/github/skills/gh-stack)
  • GitHub Check: Validate spec (plugins/frontend-design/skills/astro)
  • GitHub Check: Validate spec (plugins/frontend-design/skills/web-design-guidelines)
  • GitHub Check: Validate spec (plugins/vibe-coding/skills/jargon-free-voice)
  • GitHub Check: Validate spec (plugins/gitops-kubernetes/skills/gitops-tenant-onboarding)
  • GitHub Check: Validate spec (plugins/go/skills/golang-pro)
  • GitHub Check: Validate spec (plugins/gitops-kubernetes/skills/gitops-cluster-debug)
  • GitHub Check: Validate spec (plugins/agentic-engineering/skills/product-engineering)
  • GitHub Check: Validate spec (plugins/github/skills/gh-cli)
  • GitHub Check: Validate spec (plugins/github/skills/github-issues)
  • GitHub Check: Validate spec (plugins/engineering-practices/skills/git-commit)
  • GitHub Check: Validate spec (plugins/engineering-practices/skills/ways-of-working)
  • GitHub Check: Validate spec (plugins/engineering-practices/skills/conventional-release)
  • GitHub Check: Validate spec (plugins/github/skills/github-actions-docs)
  • GitHub Check: Validate spec (plugins/agentic-engineering/skills/agent-instructions)
  • GitHub Check: Validate spec (plugins/agentic-engineering/skills/portfolio-maintenance)
  • GitHub Check: Validate spec (plugins/agentic-engineering/skills/self-improvement)
  • GitHub Check: Lint scripts
🧰 Additional context used
📓 Path-based instructions (2)
Any change to the plugin set updates both manifests in the same PR Edit both together.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • .github/plugin/marketplace.json
  • .claude-plugin/marketplace.json
README and manifests stay in lockstep.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • .github/plugin/marketplace.json
  • .claude-plugin/marketplace.json
🪛 ast-grep (0.45.2)
plugins/agentic-engineering/scripts/surveyor-forge-readonly.test.sh

[error] 267-267: A variable, parameter expansion, or command-substitution result is passed as the command string to bash -c / sh -c, so its value is re-parsed by the shell. If any part of that value is attacker-controlled (arguments, environment, file contents, network output), it allows arbitrary command execution. Do not interpolate dynamic data into -c: pass the script as a fixed literal and forward untrusted values as positional arguments (bash -c 'program ""' _ "$value"), invoke the target program directly with proper quoting, or restrict input to a validated allowlist first.
Context: "$cmd"
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(bash-c-variable-injection-bash)

🔇 Additional comments (11)
plugins/agentic-engineering/scripts/forge-readonly-guard.sh (1)

1598-1610: LGTM!

plugins/agentic-engineering/scripts/surveyor-forge-readonly.test.sh (1)

232-296: LGTM!

plugins/agentic-engineering/.claude-plugin/plugin.json (1)

4-4: LGTM!

.claude-plugin/marketplace.json (1)

30-30: LGTM!

.github/plugin/marketplace.json (1)

30-30: LGTM!

plugins/agentic-engineering/resources/provider-neutral.desired-state.json (1)

21-21: LGTM!

Also applies to: 64-64

plugins/agentic-engineering/agents/portfolio-surveyor.agent.md (1)

475-481: LGTM!

Also applies to: 487-487

plugins/agentic-engineering/README.md (1)

299-307: LGTM!

scripts/validate-manifests.sh (1)

471-471: LGTM!

scripts/validate-manifests.test.sh (1)

699-699: LGTM!

plugins/agentic-engineering/plugin.json (1)

4-4: 🗄️ Data Integrity & Integration

Release-version parity is consistent.

All four metadata surfaces advertise 5.0.1. Each marketplace contains exactly one matching agentic-engineering entry.


📝 Walkthrough

Walkthrough

The read-only guard now denies bare default-branch classifier probes while returning a JSON-encoded sibling path when available. The surveyor decodes this path as data and retries the command through the guard. Missing or invalid discovery data produces QUERY-UNKNOWN. Tests cover relocated installations and failure cases. Documentation and validation contracts describe the updated invocation rules. Plugin versions and desired-state checksums are updated.

Merge Risk: ⚪ Minimal · up to d0662

The surveyor can now discover and invoke the installed classifier for remote default-branch checks while preserving guarded execution and fail-closed unknown results. The covered discovery and denial behavior leaves no merge-blocking risk.


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Docstring Coverage ❌ Error Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 4 files. (7 skipped: 7… 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 clearly identifies the primary change: exposing the guarded classifier path for the agentic-engineering plugin.
Description check ✅ Passed The description directly explains the classifier path discovery fix, retained guard restrictions, tests, and linked issue.
Linked Issues check ✅ Passed The changes address issue #200. They provide documented bounded discovery, return a trusted classifier path without requiring the plugin version, preserve denial of offline and malformed calls, use th…
Out of Scope Changes check ✅ Passed The changes remain within scope. Manifest version bumps, documentation, desired-state digests, validation fixtures, guard logic, adapter guidance, and relocation tests all support the classifier disco…
Full details: Docstring Coverage

Explanation

Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 4 files. (7 skipped: 7 unsupported.)


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.

@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

@coderabbitai review

Please review current full head 966c942. The previous review's concrete docstring finding is fixed: classify_segment now documents its two arguments, admission/denial behavior, and denied discovery. The generated guard digest was refreshed; all 393 guard and 35 adapter cases and actual manifest validation pass again.

Disposition of the ast-grep context warning in comment 5560626741: the test's bash -c exercises the exact command string accepted by the real guard. Before that call, the decoded path must equal the expected owned fixture path, jq @sh makes it one literal shell word, and the unchanged guard admits only that exact sibling and fixed remote-mode arguments. There is no unchecked dynamic command execution. The relocation case includes literal quotes and dollar signs and produces the expected named workflow result. No behavior changed in this documentation-only follow-up.

@coderabbitai

coderabbitai Bot commented Sep 6, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

head='966c9422150d3df210053c9c9258b35c66c792d1'
git rev-parse --verify "${head}^{commit}"
base="$(git merge-base "${head}" origin/main 2>/dev/null || git merge-base "${head}" main)"
printf 'BASE=%s\nHEAD=%s\n' "$base" "$head"

git diff --stat "$base" "$head"
git diff --name-status "$base" "$head"

printf '\n--- changed diff ---\n'
git diff --find-renames --find-copies --unified=80 "$base" "$head" -- \
  plugins/agentic-engineering/scripts/forge-readonly-guard.sh \
  plugins/agentic-engineering/scripts/surveyor-forge-readonly.test.sh \
  plugins/agentic-engineering/agents/portfolio-surveyor.agent.md \
  plugins/agentic-engineering/README.md \
  plugins/agentic-engineering/resources/provider-neutral.desired-state.json \
  scripts/validate-manifests.sh \
  scripts/validate-manifests.test.sh \
  plugins/agentic-engineering/.claude-plugin/plugin.json \
  plugins/agentic-engineering/plugin.json \
  .claude-plugin/marketplace.json \
  .github/plugin/marketplace.json

printf '\n--- guard outline ---\n'
ast-grep outline plugins/agentic-engineering/scripts/forge-readonly-guard.sh --items all || true

printf '\n--- relevant guard symbols and calls ---\n'
rg -n -C 12 -e '(^|[[:space:]])classify_segment[[:space:]]*\(' \
  -e 'DEFAULT_BRANCH_CLASSIFIER' \
  -e 'classifier-path-json:' \
  -e 'classify-default-branch-ci-runs\.sh' \
  plugins/agentic-engineering/scripts/forge-readonly-guard.sh

printf '\n--- relevant test construction and assertions ---\n'
rg -n -C 15 -e 'bash[[:space:]]+-c' \
  -e 'jq[[:space:]].*`@sh`' \
  -e 'classifier-path-json:' \
  -e 'expected.*classifier|classifier.*expected' \
  -e 'classify-default-branch-ci-runs\.sh' \
  plugins/agentic-engineering/scripts/surveyor-forge-readonly.test.sh

printf '\n--- surveyor discovery instructions ---\n'
rg -n -C 12 -e 'classifier-path-json:' \
  -e 'classify-default-branch-ci-runs\.sh' \
  -e 'QUERY-UNKNOWN' \
  -e 'literal' \
  plugins/agentic-engineering/agents/portfolio-surveyor.agent.md \
  plugins/agentic-engineering/README.md

Length of output: 174919


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- commits and follow-up diff ---'
git log --oneline --decorate -12 966c9422150d3df210053c9c9258b35c66c792d1
parent="$(git rev-parse 966c9422150d3df210053c9c9258b35c66c792d1^)"
printf 'PARENT=%s\n' "$parent"
git diff --check "$parent" 966c9422150d3df210053c9c9258b35c66c792d1
git diff --unified=30 "$parent" 966c9422150d3df210053c9c9258b35c66c792d1 -- \
  plugins/agentic-engineering/scripts/forge-readonly-guard.sh \
  plugins/agentic-engineering/resources/provider-neutral.desired-state.json

printf '%s\n' '--- guard parser and classifier admission ---'
rg -n -C 10 -e '^(tokenize_segment|scan_segments|classify_default_branch_ci|deny|die)[[:space:]]*\(' \
  -e 'DEFAULT_BRANCH_CLASSIFIER' \
  -e 'not the guarded remote-mode shape' \
  plugins/agentic-engineering/scripts/forge-readonly-guard.sh

printf '%s\n' '--- adapter guard invocation and exit mapping ---'
ast-grep outline plugins/agentic-engineering/scripts/surveyor-forge-readonly.sh --items all || true
rg -n -C 12 -e 'SURVEYOR_FORGE_READONLY_GUARD' \
  -e 'permissionDecisionReason' \
  -e 'exit ' \
  -e -- '--command' \
  plugins/agentic-engineering/scripts/surveyor-forge-readonly.sh

printf '%s\n' '--- classifier argument parsing and remote API call ---'
ast-grep outline plugins/agentic-engineering/scripts/classify-default-branch-ci-runs.sh --items all || true
rg -n -C 12 -e '^(main|usage|die)[[:space:]]*\(' \
  -e -- '--input' \
  -e -- '--repo' \
  -e -- '--branch' \
  -e -- '--head-sha' \
  -e 'gh api' \
  plugins/agentic-engineering/scripts/classify-default-branch-ci-runs.sh

printf '%s\n' '--- desired-state digest verification ---'
expected_guard="$(jq -r '.spec.source.requiredRuntimeAssets[] | select(.path == "scripts/forge-readonly-guard.sh") | .sha256' plugins/agentic-engineering/resources/provider-neutral.desired-state.json)"
expected_agent="$(jq -r '.spec.roles["portfolio-surveyor"].definitionSha256' plugins/agentic-engineering/resources/provider-neutral.desired-state.json)"
actual_guard="$(sha256sum plugins/agentic-engineering/scripts/forge-readonly-guard.sh | awk '{print $1}')"
actual_agent="$(sha256sum plugins/agentic-engineering/agents/portfolio-surveyor.agent.md | awk '{print $1}')"
printf 'guard expected=%s actual=%s\nagent expected=%s actual=%s\n' \
  "$expected_guard" "$actual_guard" "$expected_agent" "$actual_agent"
[ "$expected_guard" = "$actual_guard" ]
[ "$expected_agent" = "$actual_agent" ]

printf '%s\n' '--- manifest version consistency ---'
jq -r '.version' \
  plugins/agentic-engineering/plugin.json \
  plugins/agentic-engineering/.claude-plugin/plugin.json
jq -r '.plugins[] | select(.name == "agentic-engineering") | .version' \
  .claude-plugin/marketplace.json .github/plugin/marketplace.json

Length of output: 18279


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- command scanner and tokenizer ---'
sed -n '294,510p' plugins/agentic-engineering/scripts/forge-readonly-guard.sh
sed -n '508,690p' plugins/agentic-engineering/scripts/forge-readonly-guard.sh

printf '%s\n' '--- classifier admission ---'
sed -n '1385,1430p' plugins/agentic-engineering/scripts/forge-readonly-guard.sh

printf '%s\n' '--- adapter ---'
cat -n plugins/agentic-engineering/scripts/surveyor-forge-readonly.sh
printf '%s\n' '--- adapter guard call locations ---'
rg -n -F -C 10 -- '--command' plugins/agentic-engineering/scripts/surveyor-forge-readonly.sh
rg -n -F -C 10 -- 'permissionDecisionReason' plugins/agentic-engineering/scripts/surveyor-forge-readonly.sh

printf '%s\n' '--- classifier argument parsing and remote execution ---'
rg -n -F -C 12 -- '--repo' plugins/agentic-engineering/scripts/classify-default-branch-ci-runs.sh
rg -n -F -C 12 -- '--input' plugins/agentic-engineering/scripts/classify-default-branch-ci-runs.sh
rg -n -F -C 12 -- 'gh api' plugins/agentic-engineering/scripts/classify-default-branch-ci-runs.sh

printf '%s\n' '--- desired-state digest verification ---'
expected_guard="$(jq -r '.spec.source.requiredRuntimeAssets[] | select(.path == "scripts/forge-readonly-guard.sh") | .sha256' plugins/agentic-engineering/resources/provider-neutral.desired-state.json)"
expected_agent="$(jq -r '.spec.roles["portfolio-surveyor"].definitionSha256' plugins/agentic-engineering/resources/provider-neutral.desired-state.json)"
actual_guard="$(sha256sum plugins/agentic-engineering/scripts/forge-readonly-guard.sh | awk '{print $1}')"
actual_agent="$(sha256sum plugins/agentic-engineering/agents/portfolio-surveyor.agent.md | awk '{print $1}')"
printf 'guard expected=%s actual=%s\nagent expected=%s actual=%s\n' \
  "$expected_guard" "$actual_guard" "$expected_agent" "$actual_agent"
test "$expected_guard" = "$actual_guard"
test "$expected_agent" = "$actual_agent"

printf '%s\n' '--- manifest version consistency ---'
jq -r '.version' \
  plugins/agentic-engineering/plugin.json \
  plugins/agentic-engineering/.claude-plugin/plugin.json
jq -r '.plugins[] | select(.name == "agentic-engineering") | .version' \
  .claude-plugin/marketplace.json .github/plugin/marketplace.json

Length of output: 29885


@devantler Full review complete for 966c9422150d3df210053c9c9258b35c66c792d1.

I found no actionable correctness, security, or maintainability issues.

classify_segment now documents both arguments, admission and denial behavior, and denied discovery. The discovery probe remains denied. Its JSON hint contains only the guard-owned executable sibling path.

The guard still admits only the exact absolute sibling path and the fixed remote-mode argument shape. Offline --input remains denied.

The relocation test validates the decoded path against the owned fixture path before it applies jq @sh`` and calls bash -c. The same command first passes through the real adapter and guard. The prior dynamic-command warning does not apply.

The desired-state digests match the guard and surveyor files. The plugin and marketplace versions are consistently 5.0.1.


✏️ Learnings added
Learnt from: devantler
URL: https://github.com/devantler-tech/agent-plugins/pull/205

Timestamp: 2026-09-06T16:43:16.993Z
Learning: In `plugins/agentic-engineering/scripts/surveyor-forge-readonly.test.sh`, the relocated classifier test validates the decoded `classifier-path-json:` value against the fixture-owned absolute classifier path before it uses `jq -Rs 'sh'` to construct the command. It then submits that exact command through `surveyor-forge-readonly.sh` and the real guard before `bash -c` executes it. The guard admits only its exact classifier sibling with the fixed `--repo`, `--branch`, and full `--head-sha` remote-mode shape.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

@codex review

Please review current full head 966c942. CodeRabbit completed the previous head and its documentation finding is fixed, but its current-head request is explicitly rate limited (comment 5560662473), so that response does not satisfy current-head review.

The change exposes the guard-owned classifier path as JSON in a denied discovery response, preserving the exact executable and remote-argument restrictions. Tests exercise discovery, adapter transport, quoting, relocation, actual classifier execution, and missing/unexecutable helpers. Local verification remains 393 guard, 35 adapter, 18 classifier, and 202 manifest cases, with ShellCheck, digests, version parity, and a live guarded CI read passing.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 6, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-06T16:48:22.514193Z 966c942 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: 966c942215

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@devantler
devantler marked this pull request as ready for review September 6, 2026 16:54
@devantler
devantler merged commit 6c62e1f into main Sep 6, 2026
41 checks passed
@devantler
devantler deleted the codex/surveyor-classifier-resolution-200 branch September 6, 2026 16:54
@github-project-automation github-project-automation Bot moved this from 🫴 Ready to ✅ Done in 🌊 Project Board Sep 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

The surveyor cannot resolve the default-branch CI classifier's admitted path, so rung 0 is blind on every repository

1 participant