Skip to content

feat(cli): read workspace packages for stale CLI pins, and report them on info - #446

Merged
theCodeDrift merged 2 commits into
mainfrom
openspec/stale-cli-pins-workspaces
Oct 6, 2026
Merged

theCodeDrift merged 2 commits into
mainfrom
openspec/stale-cli-pins-workspaces

Conversation

@theCodeDrift

Copy link
Copy Markdown
Member

Follow-up to #443. Two gaps were left out of it.

Workspace packages. findStalePins read only the root package.json. In a monorepo the pin usually lives in a workspace package, and that package's CI job is the one that fails with SCAFFOLD_VERSION_MISMATCH after an upgrade migrates .taskless/. It now also reads the packages declared in pnpm-workspace.yaml (packages) and in the workspaces field (array or { packages }).

  • Patterns are expanded with Node's built-in fs.glob, which the CLI already uses, so no dependency is added. ! excludes, absolute and .. patterns are skipped, node_modules/.git are never searched, and it stops after 500 manifests.
  • The installed version is found the way Node finds it: the package's own node_modules link first (pnpm), then each parent up to the root (npm/yarn hoisting).
  • Each pin carries a new manifest field (packages/app/package.json), separate from location, which still names only the field. Notice lines name the manifest every time, including the root.

info --json reports pinnedCli in the same shape as init --json, and plain info lists the pins. The update recipe (topic v14) reads the pins from info --json, the read-only command it already runs in step 1, rather than re-running init. info recipe v2, init recipe v4.

The OpenSpec change stale-cli-pins-workspaces is archived in this PR. It changes the cli-init pin requirement (restated in full under the same title) and adds one cli requirement for info. An archive dry-run kept every existing scenario and added six.

The unreleased init-stale-cli-pins changeset is extended rather than adding a second one: latest is 0.11.2, so #443 and this ship as one patch release note in 0.12.0.

Delivery shape: single PR.

Fixes #445

@theCodeDrift

Copy link
Copy Markdown
Member Author

@claude /review

@claude

claude Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Claude finished @theCodeDrift's task in 3m 37s —— View job


Review: feat(cli): read workspace packages for stale CLI pins, and report them on info

Review mode: incremental — read 0 prior review thread(s) before reviewing. .prior-review.json held no threads, reviews, or comments beyond the trigger itself, so this is the first review of this PR — the whole diff was assessed fresh.

  • Read .prior-review.json (empty — first review)
  • Gather PR diff and changed files (gh pr diff, gh pr view)
  • Review workspace-pin discovery logic (pinned-cli.ts)
  • Review info command / schema changes
  • Review agent recipe doc changes (info.md/init.md/update.md)
  • Review OpenSpec archive/spec deltas
  • Review tests

Summary

Solid, well-tested change. I traced the workspace-glob logic (listWorkspaceManifests, readInstalledVersion, findPinsIn) against the new spec scenarios and the added tests in pinned-cli.test.ts/info.test.ts; the ../absolute-path/node_modules guards, own-link-before-hoisted resolution, and root-then-sorted-workspace ordering all check out. I also diffed the archived cli-init delta against the live openspec/specs/cli-init/spec.md text — the MODIFIED requirement is restated in full with every prior scenario intact plus the three new ones, so none of the archive-fidelity failure modes called out in CLAUDE.md apply here. yaml and fs.glob are both pre-existing patterns in this codebase (frontmatter.ts, detect/scan.ts), so no new dependency risk.

Two [New] items worth a look, both low severity:

  1. packages/cli/src/install/pinned-cli.ts:209-233 — listWorkspaceManifests shares one read/500 budget across every pattern, including negated (!...) ones. A broad exclude pattern that matches many directories (e.g. !**/fixtures) consumes the same cap as inclusion patterns, so on a large tree it's possible for legitimate workspace packages appearing later in the pattern list to go unread — not because they don't exist, but because the budget was spent on exclusions. Given the cap exists for "a tree this large has nothing useful to say anyway" (per the doc comment), this is a minor edge case rather than a functional bug, but it means the silence isn't always "nothing stale," it can be "ran out of budget on exclusions."

  2. packages/cli/src/agent/update.md:68-70 — the parenthetical (init printed a migration, or init --json carried migrated with from above 0; if you did not see that output, assume it did) asks the agent to default to treating the session as having migrated when it can't verify that. info --json (what step 4 now reads) doesn't carry a migrated field at all — only init --json does — so an agent that enters this recipe without having seen init's own output has no way to confirm or refute the assumption. Defaulting to the more urgent framing is a defensible conservative choice, but the agent is then instructed to tell the user specific things ("this upgrade migrated .taskless/ from schema version M to N") that it may not actually know, since getPinnedCliNotice's schema-version detail comes from init, not from this recipe's guidance alone. Worth double-checking this doesn't lead to an agent fabricating schema numbers it never read.

No correctness issues found in the detector, the info/init wiring, the schema, or the recipe JSON shapes. Test coverage for the new workspace behavior (pnpm-workspace.yaml, workspaces array/{packages}, negation, ** vs node_modules, own-link-vs-hoisted resolution, malformed root fallback) looks thorough.

@theCodeDrift

Copy link
Copy Markdown
Member Author

Re: @claude — "Review: feat(cli): read workspace packages for stale CLI pins, and report them on info"
#446 (comment)

Both points fixed in 91edd99. (1) Only included workspace manifests count toward the 500 cap now, so a broad ! exclusion can't use up the budget. (2) update (topic v15) still treats the bump as required when the agent didn't see init's output, but tells it to say it can't tell whether .taskless/ was migrated and not to name schema versions it hasn't read.

— AI Coding Agent

@theCodeDrift
theCodeDrift merged commit e6aac91 into main Oct 6, 2026
4 checks passed
@theCodeDrift
theCodeDrift deleted the openspec/stale-cli-pins-workspaces branch October 6, 2026 02:27
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.

Stale CLI pins: read workspace packages, and report pinnedCli on info --json

1 participant