Skip to content

test: add a sync-local regression suite and wire up CI - #17

Merged
kyle-sexton merged 2 commits into
mainfrom
claude/repo-code-tidying-simplify-69q9be
Aug 30, 2026
Merged

test: add a sync-local regression suite and wire up CI#17
kyle-sexton merged 2 commits into
mainfrom
claude/repo-code-tidying-simplify-69q9be

Conversation

@kyle-sexton

Copy link
Copy Markdown
Contributor

Closes the two gaps #16 left open: the repo had no tests and no CI, and the PSScriptAnalyzer dependency that PR started relying on was recorded nowhere.

scripts/test-sync-local.sh — 49 assertions

Every case runs both twins against the same fixture and asserts the same exit code and byte-identical stdout, because a divergence between documented parity twins is itself a defect. Only stdout is compared — PowerShell wraps stderr in its own exception frame, so the twins' error text matches while their framing does not. pwsh is optional: without it the PowerShell half reports as skipped and the bash half still runs. Fixtures build under $TMPDIR and HOME is redirected per case, so it never touches a real ~/.cursor.

It pins the defects found the hard way in #16every one of which passed a linter before it was found:

Pinned Was
Traversal via plugin name and entry source rm -rf outside the plugins root
metadata.pluginRoot escaping the repo moved the base of every later join
Empty plugins/ and "plugins": [] exit 0 printing Synced (0) — a false CI pass
Nameless marketplace entry raw Python traceback
Prefix-name sort order ai-briefing sorted before ai
Symlinked plugin directory installed a symlink into the plugins root

scripts/validate-manifests.mjs

Validates against Cursor's published schemas, not its prose reference. The two disagree in both directions: the prose lists plugin-entry fields the schema forbids, and marks owner required where the schema does not — so a prose-derived checklist would pass files a validator rejects.

Verified both ways: it passes the current manifests and fails a fixture carrying category/tags on a marketplace entry, which is exactly the state this repo was in before #16.

.github/workflows/ci.yml

Four jobs — shell (shellcheck + the suite), powershell (PSScriptAnalyzer, printing -SuppressedOnly so suppressions stay visible rather than silently disappearing), manifests (jq + schemas fetched at run time, so an upstream schema change surfaces here), and links (internal markdown only).

Two deliberate exclusions, both to avoid failures that say nothing about the repo:

  • External URLs are not checked. GitHub and cursor.directory answer CI runners with 403/429.
  • The standards-synced root files are not re-linted. They're validated upstream and must not be hand-edited here.

The link checker strips inline code spans before extracting links — without that it false-positives on ^[a-z0-9]([a-z0-9.-]*[a-z0-9])?$, which looks exactly like ](...) to a naive matcher. Verified it still catches a genuinely broken link.

AGENTS.md

Replaced "no test framework" with how to run the suite, and added a table of the tools that are not preinstalled and how to install each — keeping runtime deps (bash, git, python3, jq) separate from checking deps (shellcheck, pwsh, PSScriptAnalyzer, node+ajv). That was the specific gap: #16 came to depend on PSScriptAnalyzer with nothing recording it.

The existing claim that pwsh is absent from the base image is untouched — it's scoped to Cursor Cloud's image, which isn't observable from a dev container. Note GitHub's runners do preinstall pwsh, so CI exercises both twins even where Cursor Cloud may not.

.shellcheckrc scope widened to scripts/*.sh. Nothing is disabled and no # shellcheck disable= directive exists in the tree — the SC2016 the new script would have tripped was designed out (pwsh --version) rather than suppressed.

Verification

Check Result
shellcheck scripts/*.sh 0 findings
bash -n clean
PSScriptAnalyzer 0 findings
Regression suite 49/49, both twins
Manifest validator passes real manifests, fails a bad fixture
Internal links 0 broken
Workflow YAML parses; every step has uses/run

Limits

CI itself has not run yet — it can only execute once this is on GitHub. I validated the workflow by parsing the YAML and running every job's commands locally; actionlint was not available in this container, so the workflow file has not been linted by the tool that would normally check it. Windows behavior of the .ps1 remains unverified — the suite runs both twins on Linux only.


Generated by Claude Code

Closes the two gaps the simplification sweep left open: the repo had no tests
and no CI, and the PSScriptAnalyzer dependency the sweep started relying on was
recorded nowhere.

scripts/test-sync-local.sh — 49 assertions, all passing. Every case runs BOTH
twins against the same fixture and asserts the same exit code and byte-identical
stdout, because a divergence between documented parity twins is itself a defect.
Only stdout is compared: PowerShell wraps stderr in its own exception frame, so
the twins' error text matches while their framing does not. pwsh is optional --
without it the PowerShell half reports as skipped and the bash half still runs.
Fixtures are built under $TMPDIR and HOME is redirected per case, so the suite
never touches a real ~/.cursor.

It pins the defects that were found the hard way, each of which passed a linter
before it was found:
  - path traversal via a plugin `name` and via an entry `source`
  - a metadata.pluginRoot that escapes the source repo
  - an empty plugins/ dir and "plugins": [], which used to exit 0 printing
    "Synced (0)" -- a false pass in CI
  - a nameless marketplace entry, which used to dump a Python traceback
  - prefix-name sort order (ai before ai-briefing)
  - a symlinked plugin directory, which must install as a REAL directory

scripts/validate-manifests.mjs — validates the manifests against Cursor's
PUBLISHED schemas rather than its prose reference. The two disagree in both
directions: the prose lists plugin-entry fields the schema forbids, and marks
`owner` required where the schema does not. A prose-derived checklist would pass
files a validator rejects. Verified both ways -- it passes the current manifests
and fails a fixture carrying `category`/`tags` on a marketplace entry, which is
exactly the state this repo was in before the sweep.

.github/workflows/ci.yml — four jobs: shell (shellcheck + the regression suite),
powershell (PSScriptAnalyzer, printing -SuppressedOnly so suppressions stay
visible rather than silently disappearing), manifests (jq + the schemas, fetched
at run time so an upstream schema change surfaces here), and links (internal
markdown only).

Two deliberate exclusions in CI, both to avoid failures that say nothing about
the repository: external URLs are not checked, because GitHub and cursor.directory
answer CI runners with 403/429; and the repo-root files synced from
melodic-software/standards are not re-linted, since they are validated upstream
and must not be hand-edited here.

The link checker strips inline code spans before extracting links. Without that
it reports a false positive on the plugin-name pattern
^[a-z0-9]([a-z0-9.-]*[a-z0-9])?$, which looks exactly like ](...) to a naive
matcher. Verified it still catches a genuinely broken link.

AGENTS.md — replaced "no test framework" with how to run the suite, and added a
table of which lint/test tools are NOT preinstalled and how to install each
(shellcheck, pwsh, PSScriptAnalyzer, node+ajv), keeping the runtime dependencies
(bash, git, python3, jq) separate from the checking dependencies. Documents the
schema-vs-prose conflict, the zero-findings PSScriptAnalyzer rule and its
justification requirement, and what CI does and does not cover. The existing
claim about pwsh being absent from the base image is untouched: it is scoped to
Cursor Cloud's image, which is not observable from a dev container.

.shellcheckrc scope widened from sync-local.sh to scripts/*.sh so the new script
is covered; nothing is disabled and no `# shellcheck disable=` directive exists
in the tree. The SC2016 that the new script would have tripped was designed out
(pwsh --version) rather than suppressed.

Verified: shellcheck 0 across scripts/*.sh; bash -n clean; PSScriptAnalyzer 0;
test suite 49/49 with both twins; manifest validator passes real manifests and
fails a bad fixture; link check 0 broken; workflow YAML parses and every step
carries a uses/run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Ejst51HQobnYvjLcW1bgJ
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 30, 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-08-30T23:48:56.166561Z 3b9fd3d PR opened
ℹ️ 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.

The first CI run failed all four jobs at "Set up job", before any repository
code executed:

  The action actions/checkout@v4 is not allowed in
  melodic-software/cursor-plugins because all actions must be pinned to a
  full-length commit SHA.

That is an organization policy (this repo's settings are managed by
melodic-software/github-iac), not a defect in the workflow logic. A version tag
is mutable, so pinning to one lets a compromised or retagged release run with
the workflow's permissions; the policy forecloses that.

Pinned to fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09, resolved from the upstream
repository rather than copied from memory: refs/tags/v5 and refs/tags/v5.1.0
both dereference to that commit, and it is 40 hex characters. The version is
recorded in a comment above each use so the pin stays readable and can be
bumped deliberately.

Also moves from v4 to v5 while pinning, since the pin is being written fresh.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Ejst51HQobnYvjLcW1bgJ
@kyle-sexton
kyle-sexton merged commit b5d7d91 into main Aug 30, 2026
5 checks passed
@kyle-sexton
kyle-sexton deleted the claude/repo-code-tidying-simplify-69q9be branch August 30, 2026 23:48

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3b9fd3d214

ℹ️ 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".

Comment thread .github/workflows/ci.yml

on:
push:
branches: [main]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Run CI on pushes to every branch

The branches: [main] filter prevents this workflow from running when changes are pushed to any feature branch without an open PR, despite both the workflow comment and AGENTS.md specifying that these checks run on every push and pull request. Remove the branch filter (or explicitly include all intended branches) so pre-PR pushes receive the documented validation.

AGENTS.md reference: AGENTS.md:L97-L101

Useful? React with 👍 / 👎.

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.

2 participants