Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 195 additions & 0 deletions just/phenotype.just
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
# phenotype.just — shared recipe library for Phenotype-org repositories
#
# Usage (in a consumer repo's justfile):
# import "phenotype.just"
#
# All recipes here are workspace-agnostic; they auto-detect the build system
# (cargo / npm / pnpm / yarn / bun / uv / poetry / go / mix) and run the
# appropriate command. Recipes that are stack-specific (e.g. `register-startmenu`
# for Electrobun) live in the consumer's justfile, NOT here.
#
# Versioning: SemVer. Breaking a recipe signature is a major bump.

# === variables ===

# Detect the build system by walking up from the justfile dir.
# Returns one of: cargo | npm | pnpm | yarn | bun | uv | poetry | go | mix | none
build_system := `\
if [ -f Cargo.toml ]; then echo cargo; \
elif [ -f package.json ] && [ -f pnpm-lock.yaml ]; then echo pnpm; \
elif [ -f package.json ] && [ -f yarn.lock ]; then echo yarn; \
elif [ -f package.json ] && [ -f bun.lockb ]; then echo bun; \
elif [ -f package.json ] && [ -f bun.lock ]; then echo bun; \
elif [ -f package.json ]; then echo npm; \
elif [ -f pyproject.toml ] && [ -f uv.lock ]; then echo uv; \
elif [ -f pyproject.toml ] && [ -f poetry.lock ]; then echo poetry; \
elif [ -f pyproject.toml ]; then echo uv; \
elif [ -f go.mod ]; then echo go; \
elif [ -f mix.exs ]; then echo mix; \
else echo none; fi`

# Recipe to invoke for "build".
build_cmd := if build_system == "cargo" { "cargo build --workspace" } \
else if build_system == "pnpm" { "pnpm -r build" } \
else if build_system == "yarn" { "yarn workspaces run build" } \
else if build_system == "bun" { "bun run build" } \
else if build_system == "npm" { "npm run build --workspaces --if-present" } \
else if build_system == "uv" { "uv sync --all-extras" } \
else if build_system == "poetry" { "poetry install --all-extras" } \
else if build_system == "go" { "go build ./..." } \
else if build_system == "mix" { "mix compile" } \
else { "echo 'no build system detected' && exit 1" }

# Recipe to invoke for "test".
test_cmd := if build_system == "cargo" { "cargo test --workspace" } \
else if build_system == "pnpm" { "pnpm -r test" } \
else if build_system == "yarn" { "yarn workspaces run test" } \
else if build_system == "bun" { "bun test" } \
else if build_system == "npm" { "npm test --workspaces --if-present" } \
else if build_system == "uv" { "uv run pytest" } \
else if build_system == "poetry" { "poetry run pytest" } \
else if build_system == "go" { "go test ./..." } \
else if build_system == "mix" { "mix test" } \
else { "echo 'no test runner detected' && exit 1" }

# Recipe to invoke for "lint".
lint_cmd := if build_system == "cargo" { "cargo clippy --workspace --all-targets -- -D warnings && cargo fmt --check" } \
else if build_system == "pnpm" { "pnpm -r lint && pnpm -r format:check" } \
else if build_system == "yarn" { "yarn workspaces run lint" } \
else if build_system == "bun" { "bun run lint" } \
else if build_system == "npm" { "npm run lint --workspaces --if-present" } \
else if build_system == "uv" { "uv run ruff check . && uv run ruff format --check ." } \
else if build_system == "poetry" { "poetry run ruff check . && poetry run ruff format --check ." } \
else if build_system == "go" { "go vet ./... && gofmt -l ." } \

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Go lint ignores formatting drift

Medium Severity

For the go build system, lint_cmd ends with gofmt -l ., which lists unformatted files but exits 0 even when output is non-empty, so just lint and just ci do not fail on formatting violations unlike other stacks that use explicit format checks.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6486ae7. Configure here.

else if build_system == "mix" { "mix format --check-formatted && mix credo --strict" } \
else { "echo 'no linter detected' && exit 1" }

# === core recipes ===

# Default: list recipes.
default:
@just --list -u

# Build the workspace.
build:
{{build_cmd}}

# Run the test suite.
test:
{{test_cmd}}

# Lint (linter + formatter check).
lint:
{{lint_cmd}}

# Auto-format all files in place.
fmt:
@just _fmt

# Stack-specific formatters.
_fmt:
@if [ "{{build_system}}" = "cargo" ]; then cargo fmt; \
elif [ "{{build_system}}" = "pnpm" ]; then pnpm -r format; \
elif [ "{{build_system}}" = "yarn" ]; then yarn workspaces run format; \
elif [ "{{build_system}}" = "bun" ]; then bun run fmt; \
elif [ "{{build_system}}" = "npm" ]; then npm run format --workspaces --if-present; \
elif [ "{{build_system}}" = "uv" ]; then uv run ruff format .; \
elif [ "{{build_system}}" = "poetry" ]; then poetry run ruff format .; \
elif [ "{{build_system}}" = "go" ]; then gofmt -w .; \
elif [ "{{build_system}}" = "mix" ]; then mix format; \
else echo "no formatter detected for {{build_system}}"; fi

# Security audits. Stack-specific.
audit:
@just _audit

_audit:
@if [ "{{build_system}}" = "cargo" ]; then \
(command -v cargo-deny >/dev/null && cargo deny check || echo "cargo-deny not installed; skip") && \

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The cargo-deny branch treats any non-zero exit from cargo deny check as if the tool were missing because of ... && cargo deny check || echo .... That masks real vulnerability failures and makes just audit/just ci pass when they should fail. Split the "tool installed" check from the command execution so actual audit findings propagate a non-zero exit code. [security]

Severity Level: Critical 🚨
-`just audit` never fails on cargo-deny findings.
- ⚠️ `just ci` reports success despite deny vulnerabilities.
- ⚠️ Local security workflows relying on just get false assurance.
Steps of Reproduction ✅
1. From the repository root `/workspace/Tasken`, run `just audit`; the entrypoint Justfile
at `justfile:14` imports `just/phenotype.just` defining the `audit` recipe at
`just/phenotype.just:102-116`.

2. Because `Cargo.toml` exists at the repo root (`/workspace/Tasken/Cargo.toml`), the
`build_system` variable at `just/phenotype.just:17-29` resolves to `"cargo"`.

3. The `audit` recipe calls `_audit` (`just/phenotype.just:106`), which in the cargo
branch executes line `108` `(command -v cargo-deny >/dev/null && cargo deny check || echo
"cargo-deny not installed; skip") && \`.

4. With `cargo-deny` installed and configured (see `deny.toml` at
`/workspace/Tasken/deny.toml`), if `cargo deny check` detects a violation it exits
non-zero; this makes `command -v ... && cargo deny check` as a whole fail, triggering the
`|| echo "cargo-deny not installed; skip"` part, which returns exit code 0 so `_audit`,
`audit`, and transitively `ci` (`just/phenotype.just:145`) all succeed even though the
deny check failed.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** just/phenotype.just
**Line:** 108:108
**Comment:**
	*Security: The cargo-deny branch treats any non-zero exit from `cargo deny check` as if the tool were missing because of `... && cargo deny check || echo ...`. That masks real vulnerability failures and makes `just audit`/`just ci` pass when they should fail. Split the "tool installed" check from the command execution so actual audit findings propagate a non-zero exit code.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

(command -v cargo-audit >/dev/null && cargo audit || echo "cargo-audit not installed; skip"); \

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Installed audit failures exit zero

High Severity

The cargo _audit and _unused wrappers use (command -v tool && tool run || echo "…not installed; skip"), so when cargo deny, cargo audit, or cargo machete is installed but the check fails, the || echo branch runs and the recipe still exits successfully. The previous justfile propagated those failures, so just audit, just unused, and just ci can now pass after real policy or vulnerability findings.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6486ae7. Configure here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The cargo audit invocation has the same masking pattern: if cargo audit finds vulnerabilities and exits non-zero, the || echo ... branch runs and returns success. This causes security checks to silently pass despite detected issues, so preserve non-zero exit codes from the audit tool. [security]

Severity Level: Critical 🚨
-`just audit` ignores non-zero cargo-audit vulnerability exits.
- ⚠️ `just ci` gives green status despite RustSec advisories.
- ⚠️ Developers running just locally get misleading security results.
Steps of Reproduction ✅
1. From `/workspace/Tasken`, run `just audit`; `justfile:14` imports
`just/phenotype.just`, where `audit` and `_audit` are defined at
`just/phenotype.just:102-116`.

2. With `Cargo.toml` present, `build_system` resolves to `"cargo"`
(`just/phenotype.just:17-29`), so `_audit` takes the cargo branch (line 107) and executes
the cargo-audit command at line 109: `(command -v cargo-audit >/dev/null && cargo audit ||
echo "cargo-audit not installed; skip");`.

3. When `cargo-audit` is installed (validated by `command -v cargo-audit >/dev/null`) and
it finds vulnerable dependencies, the `cargo audit` process exits with a non-zero status,
causing the `command -v ... && cargo audit` part to fail.

4. That failure triggers the `|| echo "cargo-audit not installed; skip"` portion, which
prints the skip message and exits 0, so `_audit`, `audit`, and any `just ci` invocation
(`just/phenotype.just:145`) all report success even when `cargo-audit` actually detected
vulnerabilities; current GitHub workflows run `cargo-audit` separately
(`.github/workflows/cargo-audit.yml`) so CI still catches issues, but the Just-based flow
is incorrect.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** just/phenotype.just
**Line:** 109:109
**Comment:**
	*Security: The `cargo audit` invocation has the same masking pattern: if `cargo audit` finds vulnerabilities and exits non-zero, the `|| echo ...` branch runs and returns success. This causes security checks to silently pass despite detected issues, so preserve non-zero exit codes from the audit tool.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

elif [ "{{build_system}}" = "uv" ] || [ "{{build_system}}" = "poetry" ]; then \
uv run pip-audit 2>/dev/null || poetry run pip-audit 2>/dev/null || echo "pip-audit not installed; skip"; \

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Poetry path runs uv first

Medium Severity

Branches keyed on build_system uv or poetry always try uv run … before poetry run …, even when detection chose poetry, so audits and unused-deps checks may run under the wrong toolchain or silently skip meaningful results.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6486ae7. Configure here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The Python audit fallback chain conflates "tool missing" with "audit failed": a real pip-audit failure from uv run can fall through to poetry run and then to echo, returning success. This suppresses detected vulnerabilities in CI. Check tool availability first, then run exactly one auditor and propagate its exit status. [security]

Severity Level: Critical 🚨
- ❌ Python `just audit` can never fail on pip-audit issues.
- ⚠️ `just ci` in Python stacks silently skips dependency vulnerabilities.
- ⚠️ Security posture weaker for uv/poetry-based Phenotype repositories.
Steps of Reproduction ✅
1. In a Phenotype repo that imports `just/phenotype.just` in its justfile and has a Python
project layout (e.g. `pyproject.toml` and `uv.lock` so `build_system` resolves to `"uv"`
or `"poetry"` via `just/phenotype.just:24-26`), run `just audit`.

2. The `audit` recipe (`just/phenotype.just:102-105`) delegates to `_audit`
(`just/phenotype.just:106-116`); for `build_system` `"uv"` or `"poetry"`, line 110 chooses
the Python branch.

3. Line 111 executes `uv run pip-audit 2>/dev/null || poetry run pip-audit 2>/dev/null ||
echo "pip-audit not installed; skip";`: if `uv run pip-audit` is available and detects
vulnerabilities, `pip-audit` exits non-zero, causing the whole `uv run pip-audit` command
to fail and the shell to fall through to `poetry run pip-audit` instead of failing.

4. If the poetry-based invocation is missing or also returns non-zero (e.g. more
vulnerabilities or failures), the pipeline falls through again to `echo "pip-audit not
installed; skip"`, which exits 0 and makes `_audit`, `audit`, and `ci`
(`just/phenotype.just:145`) succeed even though a Python auditor actually reported
dependency vulnerabilities.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** just/phenotype.just
**Line:** 111:111
**Comment:**
	*Security: The Python audit fallback chain conflates "tool missing" with "audit failed": a real `pip-audit` failure from `uv run` can fall through to `poetry run` and then to `echo`, returning success. This suppresses detected vulnerabilities in CI. Check tool availability first, then run exactly one auditor and propagate its exit status.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

elif [ "{{build_system}}" = "pnpm" ]; then pnpm audit --prod; \
elif [ "{{build_system}}" = "bun" ]; then bun audit; \
elif [ "{{build_system}}" = "npm" ]; then npm audit --omit=dev; \
elif [ "{{build_system}}" = "go" ]; then govulncheck ./... 2>/dev/null || echo "govulncheck not installed; skip"; \
else echo "no audit tool for {{build_system}}"; fi
Comment on lines +107 to +116

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

critical

The current shell logic for checking and running audit tools uses the (command -v tool && tool || echo "skip") pattern. In shell scripting, A && B || C is evaluated as (A && B) || C. If A (the command check) succeeds, but B (the audit tool) fails (e.g., because it found security vulnerabilities), the shell will proceed to execute C (echo "skip") and exit with a 0 status code. This silently masks actual security vulnerabilities and audit failures in CI.

We should use an explicit if statement to check if the command exists before running it, ensuring that any failure of the tool itself correctly propagates and fails the recipe.

For uv and poetry, we can also determine the runner prefix (uv run or poetry run) and check if the tool is installed (e.g., via --version) before running it, avoiding similar masking of failures.

    @if [ "{{build_system}}" = "cargo" ]; then \\
        if command -v cargo-deny >/dev/null 2>&1; then cargo deny check; else echo "cargo-deny not installed; skip"; fi && \\
        if command -v cargo-audit >/dev/null 2>&1; then cargo audit; else echo "cargo-audit not installed; skip"; fi; \\
    elif [ "{{build_system}}" = "uv" ] || [ "{{build_system}}" = "poetry" ]; then \\
        if [ "{{build_system}}" = "uv" ]; then RUN="uv run"; else RUN="poetry run"; fi; \\
        if $RUN pip-audit --version >/dev/null 2>&1; then $RUN pip-audit; else echo "pip-audit not installed; skip"; fi; \\
    elif [ "{{build_system}}" = "pnpm" ]; then pnpm audit --prod; \\
    elif [ "{{build_system}}" = "bun" ]; then bun audit; \\
    elif [ "{{build_system}}" = "npm" ]; then npm audit --omit=dev; \\
    elif [ "{{build_system}}" = "go" ]; then \\
        if command -v govulncheck >/dev/null 2>&1; then govulncheck ./...; else echo "govulncheck not installed; skip"; fi; \\
    else echo "no audit tool for {{build_system}}"; fi


# Find unused dependencies. Stack-specific.
unused:
@just _unused

_unused:
@if [ "{{build_system}}" = "cargo" ]; then \
(command -v cargo-machete >/dev/null && cargo machete || echo "cargo-machete not installed; skip"); \

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The unused-dependency check for cargo masks real findings: if cargo machete exits non-zero (unused deps detected), the || echo ... branch runs and returns success. That prevents unused/ci from failing when dependency hygiene issues exist. Keep the non-zero result when the tool is present and reports problems. [incorrect condition logic]

Severity Level: Major ⚠️
- ⚠️ `just unused` never fails when cargo-machete finds issues.
- ⚠️ `just ci` cannot enforce Rust dependency hygiene locally.
- ⚠️ Unused deps linger, increasing maintenance and potential attack surface.
Steps of Reproduction ✅
1. From `/workspace/Tasken`, run `just unused` or `just ci`; `justfile:14` imports
`just/phenotype.just`, where `unused` and `_unused` are defined at
`just/phenotype.just:118-128`, and `ci` depends on `unused` at line 145.

2. With `Cargo.toml` present, `build_system` evaluates to `"cargo"`
(`just/phenotype.just:17-29`), so `_unused` takes the cargo branch at line 123.

3. Line 124 executes `(command -v cargo-machete >/dev/null && cargo machete || echo
"cargo-machete not installed; skip");`: when `cargo-machete` is installed, `command -v`
succeeds and `cargo machete` runs, exiting non-zero if it finds unused dependencies (the
normal mode of operation for a linter-like tool).

4. That non-zero exit from `cargo machete` causes the whole `command -v ... && cargo
machete` expression to fail, triggering the `|| echo "cargo-machete not installed; skip"`
branch, which exits 0 so `_unused`, `unused`, and `ci` all succeed even though unused
dependencies were detected.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** just/phenotype.just
**Line:** 124:124
**Comment:**
	*Incorrect Condition Logic: The unused-dependency check for cargo masks real findings: if `cargo machete` exits non-zero (unused deps detected), the `|| echo ...` branch runs and returns success. That prevents `unused`/`ci` from failing when dependency hygiene issues exist. Keep the non-zero result when the tool is present and reports problems.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

elif [ "{{build_system}}" = "uv" ] || [ "{{build_system}}" = "poetry" ]; then \
uv run deptry . 2>/dev/null || poetry run deptry . 2>/dev/null || echo "deptry not installed; skip"; \
elif [ "{{build_system}}" = "pnpm" ]; then pnpm dlx depcheck; \
else echo "no unused-dep tool for {{build_system}}"; fi
Comment on lines +123 to +128

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Similar to the _audit recipe, the _unused recipe uses the A && B || C and A || B || C patterns, which silently mask actual unused dependency detection failures (e.g., when cargo-machete or deptry exits with a non-zero code upon finding unused dependencies).

We should use explicit if statements to check for tool availability before running them.

    @if [ "{{build_system}}" = "cargo" ]; then \\
        if command -v cargo-machete >/dev/null 2>&1; then cargo machete; else echo "cargo-machete not installed; skip"; fi; \\
    elif [ "{{build_system}}" = "uv" ] || [ "{{build_system}}" = "poetry" ]; then \\
        if [ "{{build_system}}" = "uv" ]; then RUN="uv run"; else RUN="poetry run"; fi; \\
        if $RUN deptry --version >/dev/null 2>&1; then $RUN deptry .; else echo "deptry not installed; skip"; fi; \\
    elif [ "{{build_system}}" = "pnpm" ]; then pnpm dlx depcheck; \\
    else echo "no unused-dep tool for {{build_system}}"; fi


# Type-check. Stack-specific.
typecheck:
@just _typecheck

_typecheck:
@if [ "{{build_system}}" = "cargo" ]; then cargo check --workspace --all-targets; \
elif [ "{{build_system}}" = "uv" ] || [ "{{build_system}}" = "poetry" ]; then \
(uv run mypy . 2>/dev/null || poetry run mypy . 2>/dev/null || echo "mypy not installed; skip"); \

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The mypy typecheck command has the same logic bug: type errors from uv run mypy are treated like "tool not installed" because of the || fallback chain and final echo. This makes typecheck pass even when type checking fails. Distinguish missing-tool detection from actual typecheck failures and return non-zero on errors. [incorrect condition logic]

Severity Level: Major ⚠️
-`just typecheck` passes even when mypy finds type errors.
- ⚠️ `just ci` in Python projects skips typecheck failures.
- ⚠️ Developers may merge code with undetected typing regressions.
Steps of Reproduction ✅
1. In a Phenotype repository that imports `just/phenotype.just` and uses a Python
toolchain (`pyproject.toml` with either `uv.lock` or `poetry.lock` so `build_system`
becomes `"uv"` or `"poetry"` per `just/phenotype.just:24-26`), run `just typecheck` or
`just ci` (which depends on `typecheck` at `just/phenotype.just:145`).

2. The `typecheck` recipe (`just/phenotype.just:130-132`) calls `_typecheck`
(`just/phenotype.just:134-142`); for `"uv"`/`"poetry"` the branch at lines 136-137 runs
`(uv run mypy . 2>/dev/null || poetry run mypy . 2>/dev/null || echo "mypy not installed;
skip");`.

3. If `uv run mypy .` is available and finds type errors, `mypy` exits with a non-zero
status, causing the first command to fail and the shell to fall through to `poetry run
mypy .` instead of failing immediately.

4. If the poetry-based mypy invocation is missing or also finds type errors, the
expression falls through again to `echo "mypy not installed; skip"`, which returns 0 so
`_typecheck`, `typecheck`, and `ci` all succeed even when static type checking failed.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** just/phenotype.just
**Line:** 137:137
**Comment:**
	*Incorrect Condition Logic: The mypy typecheck command has the same logic bug: type errors from `uv run mypy` are treated like "tool not installed" because of the `||` fallback chain and final `echo`. This makes `typecheck` pass even when type checking fails. Distinguish missing-tool detection from actual typecheck failures and return non-zero on errors.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

elif [ "{{build_system}}" = "pnpm" ]; then pnpm -r typecheck; \
elif [ "{{build_system}}" = "bun" ]; then bun run typecheck 2>/dev/null || echo "no typecheck script"; \
elif [ "{{build_system}}" = "npm" ]; then npm run typecheck --workspaces --if-present; \
elif [ "{{build_system}}" = "go" ]; then go build ./... && go vet ./...; \
else echo "no typechecker for {{build_system}}"; fi
Comment on lines +135 to +142

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The _typecheck recipe also suffers from the failure-masking issue:

  1. For uv/poetry, if mypy finds type errors and exits with a non-zero code, it falls back to echo "mypy not installed; skip" and returns 0.
  2. For bun, if bun run typecheck fails due to actual type errors, it falls back to echo "no typecheck script" and returns 0.

We should check for tool/script existence first before running them. For bun, we can check if the typecheck script is defined in package.json using grep.

    @if [ "{{build_system}}" = "cargo" ]; then cargo check --workspace --all-targets; \\
    elif [ "{{build_system}}" = "uv" ] || [ "{{build_system}}" = "poetry" ]; then \\
        if [ "{{build_system}}" = "uv" ]; then RUN="uv run"; else RUN="poetry run"; fi; \\
        if $RUN mypy --version >/dev/null 2>&1; then $RUN mypy .; else echo "mypy not installed; skip"; fi; \\
    elif [ "{{build_system}}" = "pnpm" ]; then pnpm -r typecheck; \\
    elif [ "{{build_system}}" = "bun" ]; then \\
        if grep -q '"typecheck":' package.json; then bun run typecheck; else echo "no typecheck script"; fi; \\
    elif [ "{{build_system}}" = "npm" ]; then npm run typecheck --workspaces --if-present; \\
    elif [ "{{build_system}}" = "go" ]; then go build ./... && go vet ./...; \\
    else echo "no typechecker for {{build_system}}"; fi


# Full local CI sweep: lint + test + audit + unused + typecheck.
ci: lint typecheck test audit unused
@echo "✓ all CI checks passed"

# Generate docs.
docs:
@just _docs

_docs:
@if [ "{{build_system}}" = "cargo" ]; then cargo doc --no-deps --workspace; \
elif [ "{{build_system}}" = "uv" ] || [ "{{build_system}}" = "poetry" ]; then \
uv run sphinx-build -b html docs/ docs/_build/ 2>/dev/null || \
poetry run sphinx-build -b html docs/ docs/_build/ 2>/dev/null || \
echo "no sphinx config; skip"; \
else echo "no doc generator for {{build_system}}"; fi
Comment on lines +153 to +158

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The _docs recipe also masks documentation build failures (e.g., if sphinx-build fails due to syntax or configuration errors) by falling back to echo "no sphinx config; skip".

We should check if sphinx-build is available in the environment first.

    @if [ "{{build_system}}" = "cargo" ]; then cargo doc --no-deps --workspace; \\
    elif [ "{{build_system}}" = "uv" ] || [ "{{build_system}}" = "poetry" ]; then \\
        if [ "{{build_system}}" = "uv" ]; then RUN="uv run"; else RUN="poetry run"; fi; \\
        if $RUN sphinx-build --version >/dev/null 2>&1; then $RUN sphinx-build -b html docs/ docs/_build/; else echo "no sphinx config; skip"; fi; \\
    else echo "no doc generator for {{build_system}}"; fi


# === developer-experience recipes ===

# Watch mode: rebuild on change. Stack-specific.
dev:
@just _dev

_dev:
@if [ "{{build_system}}" = "cargo" ]; then \
(command -v cargo-watch >/dev/null && cargo watch -x build -x test || \
echo "cargo-watch not installed; install with: cargo install cargo-watch"); \
elif [ "{{build_system}}" = "bun" ]; then bun run dev; \
elif [ "{{build_system}}" = "pnpm" ]; then pnpm -r dev; \
elif [ "{{build_system}}" = "npm" ]; then npm run dev --workspaces --if-present; \
else echo "no dev script for {{build_system}}"; fi
Comment on lines +167 to +173

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The _dev recipe for Cargo uses the A && B || C pattern, which can print "cargo-watch not installed" if cargo watch fails or is terminated with a non-zero exit code.

We should use an explicit if statement to check for cargo-watch availability.

    @if [ "{{build_system}}" = "cargo" ]; then \\
        if command -v cargo-watch >/dev/null 2>&1; then cargo watch -x build -x test; \\
        else echo "cargo-watch not installed; install with: cargo install cargo-watch"; fi; \\
    elif [ "{{build_system}}" = "bun" ]; then bun run dev; \\
    elif [ "{{build_system}}" = "pnpm" ]; then pnpm -r dev; \\
    elif [ "{{build_system}}" = "npm" ]; then npm run dev --workspaces --if-present; \\
    else echo "no dev script for {{build_system}}"; fi


# Clean build artifacts.
clean:
@just _clean

_clean:
@if [ "{{build_system}}" = "cargo" ]; then cargo clean; \
elif [ "{{build_system}}" = "uv" ] || [ "{{build_system}}" = "poetry" ]; then \
rm -rf .venv dist build *.egg-info .pytest_cache .mypy_cache .ruff_cache; \
elif [ "{{build_system}}" = "pnpm" ]; then pnpm clean 2>/dev/null || rm -rf node_modules; \
elif [ "{{build_system}}" = "bun" ]; then rm -rf node_modules dist build; \
elif [ "{{build_system}}" = "npm" ]; then rm -rf node_modules dist build; \
elif [ "{{build_system}}" = "go" ]; then go clean -cache -testcache; \
elif [ "{{build_system}}" = "mix" ]; then mix clean; \
else rm -rf build dist; fi

# Print detected build system (debug).
info:
@echo "build_system={{build_system}}"
@echo "build_cmd={{build_cmd}}"
@echo "test_cmd={{test_cmd}}"
@echo "lint_cmd={{lint_cmd}}"
41 changes: 14 additions & 27 deletions justfile
Original file line number Diff line number Diff line change
@@ -1,29 +1,16 @@
# Phenotype-org standard justfile
# Tasken Justfile
#
# After 2026-06-11, this justfile is a thin shell that re-exports the shared
# `phenotype.just` library (defined in just/phenotype.just). The 9 most
# common recipes (default, build, test, lint, fmt, audit, unused, ci, docs)
# are now defined once in the library and parameterized over the build
# system.
#
# Stack-specific recipes (e.g. `clean`, `dev`) stay in this file.
#
# To upgrade: pull the latest phenotype.just from the central repo, or
# vendor it as a git submodule.

import "just/phenotype.just"

default:
@just --list

build:
cargo build --workspace

test:
cargo test --workspace

lint:
cargo clippy --workspace -- -D warnings
cargo fmt --check

fmt:
cargo fmt

audit:
cargo deny check
cargo audit

unused:
cargo machete

ci: lint test audit unused

docs:
cargo doc --no-deps --workspace
Loading