Expand curated packs, task recipes, and discovery commands - #1
Conversation
Add four documentation-only examples (FieldNote, SignalWell, RepoLens, DockSense) that show product and architecture decisions for platform permissions, data contracts, CLI stability, and firmware fail-safes. Co-authored-by: Misbah K. <m.misbahkhursheed@gmail.com>
Fill every domain pack with specialists, skills, prompts, and argv-only recipes, and add backend-api, qa-testing, observability, and documentation packs. Recommend packs from repository evidence without installing them, filter catalog and task lists, and fail missing preconditions or executables before a recipe continues. Co-authored-by: Misbah K. <m.misbahkhursheed@gmail.com>
|
@cursoragent review |
|
Review is on PR #1. The pack model is sound; the problems are risk labels and contracts on new recipes. Fix before merge
Pre-existing (not this diff, still worth knowing)
Line comments are on the PR. I can apply 1–4 in a follow-up commit if you want. |
IMisbahk
left a comment
There was a problem hiding this comment.
Review
The direction matches ADR-0003: opt-in packs, argv-only recipes, recommend does not install. Uniqueness tests for current builtins, --pack/--risk filtering, and missing-precondition fail-closed look correct.
The blockers are risk labels and contracts on new recipes, not the pack registry shape.
Changes to make before merge
ml.compileall(andml.mypy) asread-only—compileallalways writes__pycache__. Defaultbalancedpolicy will mutate the tree with no confirmation.docs.grep-todo—git grepexit1on no matches is treated as task failure.- Catalog
detail— meaning changed atschemaVersion: 1(task risk is no longer a bare class; prompt hints are suffixed). - Recommend vs preconditions —
compose.yml/terraform/main.tf/docs/markers do not match the recipes they suggest;confidence: "low"is dead. - New recipes that execute project code (
node --test, Playwright, ESLint) should not inherit the oldquality.testread-onlylabel if the policy is supposed to mean “no confirmation.”
Residual (pre-existing, not introduced here)
execute()usesstdio: "inherit", so live--jsonruns are not parseable JSON; failed JSON runsreturnand exit0.- Timeout only sends
SIGTERMand can hang if the child ignores it. - Unknown
taskPolicyin.mstack/config.jsonfails open; project config can weaken a globalstrictpolicy. {{package}}interpolation is not shell injection (shell: false) but can still pass npm flags (--prefix,-g).PackRegistryrejects duplicate task IDs only; specialist/skill/prompt uniqueness is test-enforced on current data, not an invariant at register time.mergeByIdwould silently keep the first collision.
Verification
Read the feature spec, ADR-0003, and the pack/task/catalog/recommend diffs. Specialist passes: code-reviewer and security-reviewer. Did not re-run the suite in this review turn (previous turn: CLI 45 passed, integrations 35 passed / 2 skipped).
I would not merge until 1–4 are fixed. Item 5 can be a follow-up if you explicitly keep npm test as read-only for 0.6 compatibility, but new recipes should not copy that.
| ), | ||
| ], | ||
| tasks: [ | ||
| recipe({ id: "ml.compileall", description: "Byte-compile Python sources without executing them.", risk: "read-only", argv: ["python", "-m", "compileall", "-q", "."] }), |
There was a problem hiding this comment.
High: This is labeled read-only, so the default balanced policy runs it with no confirmation, but python -m compileall writes __pycache__ / .pyc and can follow symlinks.
Relabel as working-tree, or drop the recipe. ml.mypy has the same cache-write issue (.mypy_cache).
| description: `Reusable engineering template at ${item.path}.`, | ||
| })), | ||
| ...packs.flatMap((pack) => pack.tasks.map((item) => ({ kind: "task-recipes" as const, id: item.id, description: item.description, detail: item.risk }))), | ||
| ...packs.flatMap((pack) => pack.tasks.map((item) => ({ kind: "task-recipes" as const, id: item.id, description: item.description, detail: `${item.risk} · ${pack.id}` }))), |
There was a problem hiding this comment.
Medium: schemaVersion stays 1, but detail changed meaning. 0.6.0 consumers treated task detail as the risk class ("read-only") and prompt detail as argumentHint. They now get "read-only · mobile" and "${hint} · build-like-this".
Keep the old detail meaning and add fields (pack, source), or bump schemaVersion.
| ), | ||
| ], | ||
| tasks: [ | ||
| recipe({ id: "infra.tf-fmt-check", description: "Check Terraform formatting without rewriting files.", risk: "read-only", argv: ["terraform", "fmt", "-check", "-recursive"], preconditions: ["main.tf"] }), |
There was a problem hiding this comment.
Medium: Recommend markers and recipe preconditions disagree.
terraform/main.tfsuggestsinfrastructure-security, butinfra.tf-fmt-checkrequires rootmain.tf.compose.yml/docker-compose.ymlsuggest packs, butinfra.compose-configrequires onlycompose.yaml.obs.compose-confighas no compose-file precondition at all.
Align the marker list with preconditions (any of the compose filenames; main.tf or terraform/main.tf).
| tasks: [ | ||
| recipe({ id: "docs.list", description: "List tracked documentation files.", risk: "read-only", argv: ["git", "ls-files", "--", "docs", "README.md"] }), | ||
| recipe({ id: "docs.diff", description: "Show documentation diffs.", risk: "read-only", argv: ["git", "diff", "--", "docs", "README.md"] }), | ||
| recipe({ id: "docs.grep-todo", description: "Find TODO markers in documentation.", risk: "read-only", argv: ["git", "grep", "-n", "TODO", "--", "docs"] }), |
There was a problem hiding this comment.
Medium: git grep exits 1 when there are no matches, and the runner treats any non-zero as a failed step. A clean docs tree therefore fails this recipe. Empty search is success.
Map exit 1 to success, or do not use git grep's no-match status as failure. Add a docs precondition so a missing pathspec is not a generic 128.
| ], | ||
| tasks: [ | ||
| recipe({ id: "devtools.pkg-bin", description: "Show package.bin from package.json.", risk: "read-only", argv: ["npm", "pkg", "get", "bin"], preconditions: ["package.json"] }), | ||
| recipe({ id: "devtools.node-test", description: "Run Node's built-in test runner.", risk: "read-only", argv: ["node", "--test"] }), |
There was a problem hiding this comment.
Medium: qa.node-test / devtools.node-test (and web.eslint / Playwright list recipes) execute project code but are read-only, so they skip confirmation.
This extends the pre-existing quality.test pattern. For new recipes, prefer working-tree whenever the argv loads package.json scripts, test files, or JS/TS config. Keep read-only for commands that cannot execute repository code (git status, terraform fmt -check).
| function confidenceFor(reasonCount: number): RecommendationConfidence { | ||
| if (reasonCount >= 3) return "high"; | ||
| if (reasonCount >= 2) return "high"; | ||
| return "medium"; |
There was a problem hiding this comment.
Medium: low is on the public type and sort order but never produced. Two reasons and twenty reasons are both high.
Use >= 3 → high, 2 → medium, 1 → low, or drop low from the contract. The recommend test never asserts confidence.
Also: every mstack init repo has docs/, so documentation is always suggested. Cite something more specific than the init template directory.
| ), | ||
| ], | ||
| tasks: [ | ||
| recipe({ id: "systems.cargo-check", description: "Typecheck a Rust crate without running tests.", risk: "working-tree", argv: ["cargo", "check", "--locked"], preconditions: ["Cargo.toml"] }), |
There was a problem hiding this comment.
Low/medium: --locked requires Cargo.lock. Library crates that only ship Cargo.toml fail this recipe even though the precondition passes.
Drop --locked, or add Cargo.lock as a precondition. Same pattern on systems.cargo-test and graphics.cargo-clippy.
| tasks: [ | ||
| recipe({ id: "devtools.pkg-bin", description: "Show package.bin from package.json.", risk: "read-only", argv: ["npm", "pkg", "get", "bin"], preconditions: ["package.json"] }), | ||
| recipe({ id: "devtools.node-test", description: "Run Node's built-in test runner.", risk: "read-only", argv: ["node", "--test"] }), | ||
| recipe({ id: "devtools.help", description: "Print this CLI's help.", risk: "read-only", argv: ["node", "--help"] }), |
There was a problem hiding this comment.
Low: devtools.help runs node --help, not mstack help. Fix the description or point argv at the mstack binary.


Why
mstack users working across domains needed more than a thin starter catalog: most packs shipped a single specialist and no recipes, discovery could not filter or recommend, and missing toolchains failed as generic spawn errors. This change gives those users a broader, still opt-in capability surface without hosting inference or weakening argv-only execution.
What changed
backend-api,qa-testing,observability, anddocumentationpacks.mstack pack recommend(read-only evidence from well-known markers; does not install).mstack catalog --queryandmstack task list --pack/--risk.mstack status.Reasoning
The default Build Like This runtime stays lightweight. Domain depth stays behind explicit pack selection. Recommendation cites files instead of auto-installing, which preserves consent and avoids pretending heuristics are product evidence. Task recipes remain literal argv with
shell: false; no deploy, publish, or shell-script recipes were added.Verification
pnpm --filter @imisbahk/mstack test— 45 passedpnpm --filter @mstack/ai-integrations test— 35 passed, 2 skipped (Codex/OpenCode CLIs absent)pnpm typecheck— passedpnpm --filter @imisbahk/mstack build— passedpnpm --filter @imisbahk/mstack docs:generate— regenerated command referencemstack agentremains discovery-only)Scope
Non-goals: marketplace/remote packs, hosted model execution, new AI runtime adapters, packaging/publishing/deployment recipes, and automatic pack installation from heuristics.