Skip to content

feat!: add per-user role profiles - #10

Merged
altaywtf merged 14 commits into
mainfrom
feat/user-role-profiles
Aug 1, 2026
Merged

feat!: add per-user role profiles#10
altaywtf merged 14 commits into
mainfrom
feat/user-role-profiles

Conversation

@altaywtf

@altaywtf altaywtf commented Aug 1, 2026

Copy link
Copy Markdown
Member

Summary

Introduce per-Unix-user workstation, devbox, and assistant profiles while retaining personal as a compatibility alias for workstation.

This repository is a vendor-neutral macOS bootstrap framework for any person, team, or organization. The current owner is not part of its consumer-facing namespace.

Breaking change

This is intentionally not an in-place migration. Existing users must inventory and manually move former owner-specific config, reconfigure generated Git/SSH state, and retire conflicting LaunchAgents/LaunchDaemons before applying a new role. The installer does not read, rewrite, move, or delete that old state.

Follow Migrating to Role Profiles for the exact per-user checklist.

Changed

  • split Homebrew and mise state into minimal base, developer, and role-specific layers
  • make chezmoi rendering and bootstrap/verification commands profile-aware
  • keep workstation/devbox developer tooling, human Git identity, signing, GitHub CLI, and SSH behavior
  • give assistants minimal runtimes and unsigned workload Git authorship
  • keep assistant GitHub authentication as later platform-owned work
  • use generic paths and configurable local.dotfiles.* service labels
  • make legacy namespace and service transition an explicit agent/operator task
  • verify expected managed profile state instead of crawling unrelated user-home state
  • enforce the vendor-neutral boundary in repository verification

Review aids

flowchart LR
    U[Unix user] --> P{Profile}
    P --> W[workstation]
    P --> D[devbox]
    P --> A[assistant]
    W --> DEV[base + developer + workstation]
    D --> DEV2[base + developer + devbox]
    A --> MIN[base + assistant]
    MIN --> ID[unsigned workload authorship]
    ID --> AUTH[GitHub auth deferred to platform setup]
Loading

Risks

  • Existing users must complete the documented manual migration before applying this release.
  • Profile application does not uninstall old packages or credentials; migration agents inventory and clean them separately.
  • Conflicting old services must be stopped and archived explicitly before replacement installation.
  • No live agent host state, GitHub App, workspace, or persona account was changed.

Verification

  • ./scripts/verify/repo.sh
  • focused profile, Git identity, audit, installer, and LaunchDaemon contract fixtures
  • ShellCheck and Actionlint
  • Gitleaks and TruffleHog: clean

Complexity

Moderate. The release adds three explicit role layers and shared profile/service-label contracts. Automatic legacy migration, GitHub App authentication, and agent-platform orchestration are intentionally out of scope.

BREAKING CHANGE: Existing users must manually move former owner-specific config, reconfigure Git, and retire conflicting services before applying a workstation, devbox, or assistant profile. See docs/migrating-to-role-profiles.md.

Copilot AI review requested due to automatic review settings August 1, 2026 06:27

Copilot AI 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.

Pull request overview

This PR makes the dotfiles/bootstrap repo profile-aware per Unix user by introducing workstation, devbox, and assistant roles (with personal preserved as a compatibility alias for workstation). It restructures install layers (Homebrew + mise + chezmoi rendering) around those roles and adds verification/audit coverage for the new profile contracts, including a stricter assistant credential boundary.

Changes:

  • Add a shared profile resolver and make bootstrap/verify scripts consume resolved profiles (including personal -> workstation aliasing).
  • Split Homebrew into base + developer + role layers and update verification to enforce the intended layering per profile.
  • Introduce assistant-specific minimal dotfile rendering and verification to avoid persisted signing/SSH/GitHub credentials while allowing workload commit authorship.

Reviewed changes

Copilot reviewed 45 out of 45 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
scripts/verify/zsh-prompt.sh Clears Git hook environment leakage for the prompt fixture run.
scripts/verify/repo.sh Adds profile contract verification to the repo gate and updates wording from personal->workstation.
scripts/verify/profiles.sh New profile contract verifier covering layers, rendered outputs, and assistant boundary behavior.
scripts/verify/configure-git.sh Extends Git bootstrap verification to cover assistant workload identity config.
scripts/verify/brew-devbox.sh Updates expected layered bundle behavior and adds assistant bundle assertions.
scripts/verify/bootstrap.sh Makes live bootstrap verification profile-aware with developer-vs-assistant branching.
scripts/verify/assistant-git-boundary.sh New assistant user-home boundary checker for persisted credentials/signing/SSH state.
scripts/README.md Updates script docs to reflect workstation/devbox/assistant naming and entrypoints.
scripts/lib/profile.sh New shared profile normalization/resolution and per-profile layer helpers.
scripts/bootstrap/install.sh Makes install steps profile-aware; skips developer-only steps for assistants.
scripts/bootstrap/configure-power.sh Updates profile naming and keeps personal alias compatibility.
scripts/bootstrap/configure-git.sh Adds assistant profile behavior for unsigned workload identity and rejects signing/SSH auth.
scripts/bootstrap/brew-bundle.sh Implements base+developer+profile bundling and adds --print-files.
scripts/bootstrap/apply-dotfiles.sh Makes chezmoi apply profile-aware via override data and requires/derives profile.
scripts/audit/workstation.sh New workstation drift/security audit (replacing personal audit surface).
scripts/audit/personal.sh Converts personal audit into a compatibility wrapper for workstation audit.
README.md Updates top-level docs for per-user profiles and layered installs.
docs/security-audits.md Renames personal drift audit docs to workstation drift audit.
docs/profiles.md New documentation describing per-user profiles, layer model, and identity policy.
docs/mise.md Updates mise documentation for profile-rendered runtime pins and new tasks.
docs/devbox.md Notes assistant reuse of devbox boundary concepts without devbox toolchain.
docs/chezmoi.md Updates chezmoi source-state mapping and profile-based rendering guidance.
docs/bootstrap.md Extends bootstrap guide for workstation/devbox/assistant flows and assistant token transport.
docs/agent-readiness.md Updates readiness evidence and tasks to reflect profile-aware verification/audits.
CONTRIBUTING.md Updates contributor instructions for workstation naming and new Brewfile layering.
chezmoi/private_dot_local/private_bin/executable_uinaf-git-app Adds GitHub App token HTTPS wrapper for assistant Git operations.
chezmoi/private_dot_config/private_uinaf/profile.tmpl New per-user profile marker rendered by chezmoi.
chezmoi/private_dot_config/mise/config.toml.tmpl Makes runtime pins conditional on assistant vs developer profiles.
chezmoi/dot_gitconfig.tmpl Renders a minimal Git base for assistants while keeping developer auth/signing blocks for others.
chezmoi/.chezmoiignore.tmpl Excludes developer identity/editor state from assistant profile rendering.
Brewfile.workstation Renames/repurposes the former personal layer as workstation role software.
Brewfile.developer New shared developer layer used by workstation/devbox only.
Brewfile.devbox Updates comments and layering expectations for devbox role software.
Brewfile.assistant New minimal assistant role layer (browser/media/supervision).
Brewfile Slims base to identity-safe bootstrap tools only.
AGENTS.md Updates agent guidance and repo rules to the new profile/layer model.
.mise/tasks/verify/bootstrap/workstation Adds a workstation bootstrap verification task.
.mise/tasks/verify/bootstrap/personal Converts personal bootstrap verification into workstation alias.
.mise/tasks/verify/bootstrap/assistant Adds an assistant bootstrap verification task.
.mise/tasks/audit/workstation/json Adds workstation audit JSON task.
.mise/tasks/audit/workstation/_default Adds workstation audit default task.
.mise/tasks/audit/personal/json Converts personal audit JSON into workstation alias.
.mise/tasks/audit/personal/_default Converts personal audit default into workstation alias.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread chezmoi/private_dot_local/private_bin/executable_git-as-github-app Outdated
Comment thread scripts/audit/workstation.sh Outdated
Comment thread scripts/verify/profiles.sh Outdated

@cubic-dev-ai cubic-dev-ai 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.

14 issues found and verified against the latest diff

Confidence score: 2/5

  • In chezmoi/private_dot_local/private_bin/executable_uinaf-git-app, git hooks inherit the installation token and askpass marker, so any hook in a touched repo could exfiltrate credentials and bypass the intended boundary—sanitize hook environment and prevent token/askpass propagation before invoking git.
  • In scripts/verify/assistant-git-boundary.sh, boundary checks can report “clean” while real credentials remain available (~/.git-credentials and SSH2 key headers are not detected), creating a false security signal—extend the scanner to credential-store files and SSH2 private-key formats.
  • Profile/role handling across scripts/bootstrap/configure-git.sh and scripts/verify/bootstrap.sh appears inconsistent (assistant role ignored without --profile, and assistant SSH prompt check now fails), which risks misconfiguring identity and breaking assistant bootstrap verification—resolve stored profile before fallback and scope prompt assertions by profile.
  • scripts/audit/workstation.sh still emits identifying data (git name/email in default logs and Unix account in --json), so routine audit sharing can leak personal machine identity—keep pass/fail checks but redact or replace identity-bearing fields in output.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/bootstrap/install.sh">

<violation number="1" location="scripts/bootstrap/install.sh:54">
P2: The `--print-steps` mode and the actual developer execution block now each hand-maintain the same list of developer steps (trust-agent-worktrees, install-gh-extensions, install-native-pnpm/pnpm, configure-codex). Both are gated on `uinaf_profile_is_developer`, but the step list itself exists in two places that can drift. `scripts/verify/profiles.sh` only asserts against the `--print-steps` output, so if a developer step is later added to one block and forgotten in the other, `--print-steps` will silently misreport what `install.sh` actually runs. Consider deriving both from a single source (e.g., a function that enumerates the developer steps) so the preview and the real run can't diverge.</violation>

<violation number="2" location="scripts/bootstrap/install.sh:55">
P2: Custom agent: **Enforce Pragmatic Test Coverage**

The profile-aware execution branching in `install.sh` is not covered by tests beyond the `--print-steps` preview. `scripts/verify/profiles.sh` asserts step names for `--print-steps`, but no test verifies the actual conditional execution of `apply-dotfiles.sh`, `trust-agent-worktrees.sh`, `install-gh-extensions.sh`, `pnpm`, or `codex` setup. The new CLI error paths (missing `--profile`, unknown flags, invalid profile) are also untested. Please add verification tests that exercise the real install branches and the failure paths.</violation>
</file>

<file name="scripts/bootstrap/brew-bundle.sh">

<violation number="1" location="scripts/bootstrap/brew-bundle.sh:116">
P2: The `--print-files` output is derived from `uinaf_profile_brewfiles`, but the actual install sequence is maintained separately as a hand-written base + optional `Brewfile.developer` + `Brewfile.$profile` block. These two sources independently encode the layer composition for the same profile, so they can drift: adding a new shared layer (or gating one differently) in one place but not the other makes `--print-files` misrepresent what `brew-bundle.sh` actually installs. Since scripts/verify/bootstrap.sh's `check_brew_bundle` also relies on `uinaf_profile_brewfiles`, the actual install loop should ideally iterate the same source (as `--print-files` already does) to keep the preview and the real execution consistent.</violation>
</file>

<file name="scripts/verify/bootstrap.sh">

<violation number="1" location="scripts/verify/bootstrap.sh:291">
P2: Assistant bootstrap verification now fails its SSH-prompt check: `%n@%m` is configured only for users with `devbox.env`, which assistants do not receive. Restrict this check to `devbox`, or add a separate assistant prompt implementation if that behavior is intended.</violation>
</file>

<file name="scripts/bootstrap/configure-git.sh">

<violation number="1" location="scripts/bootstrap/configure-git.sh:285">
P2: A configured assistant role is ignored when this command runs without `--profile`: the empty branch defaults to `workstation` before normalization. Resolve the stored profile before choosing an interactive fallback so a profile-marked assistant cannot accidentally receive workstation Git configuration.</violation>
</file>

<file name="scripts/verify/configure-git.sh">

<violation number="1" location="scripts/verify/configure-git.sh:295">
P2: Assistant fixture is not isolated from ambient Git config: exported `GIT_CONFIG_GLOBAL` replaces `$assistant_home/.gitconfig`, so its commit can miss the workload identity or inherit signing settings. Clear global/system config overrides before applying the fixture, matching the existing assistant-boundary setup.</violation>
</file>

<file name="scripts/verify/repo.sh">

<violation number="1" location="scripts/verify/repo.sh:97">
P3: The new `profiles.sh` gate runs the `chezmoi` binary directly (in its `render_target` helper), but this section is the only `repo.sh` step that depends on chezmoi and the "required tools" checklist above the new section only checks git, bash, shellcheck, actionlint, gitleaks, and trufflehog. On an environment without chezmoi the gate now dies with `chezmoi: command not found` instead of the friendly `missing ...; install the shared Brewfile first` message every other tool gets. Consider adding a `need_command chezmoi` guard so the failure is clear and consistent.</violation>
</file>

<file name="chezmoi/private_dot_local/private_bin/executable_uinaf-git-app">

<violation number="1" location="chezmoi/private_dot_local/private_bin/executable_uinaf-git-app:32">
P1: Git hooks launched by this wrapper inherit the installation token and active askpass marker, so a hook in any operated repository can exfiltrate the token or request it from `uinaf-git-app`. This breaks the intended scoped-token boundary; isolate or disable repository hooks for wrapper invocations, or redesign token delivery so only the askpass process can access it.</violation>
</file>

<file name="scripts/audit/workstation.sh">

<violation number="1" location="scripts/audit/workstation.sh:44">
P2: `--json` remote collection includes the Unix account name, exposing identity-specific machine state despite the audit boundary. Replace it with a non-identifying value or omit this field.</violation>

<violation number="2" location="scripts/audit/workstation.sh:140">
P2: Default audit output exposes the workstation's configured Git name and email in logs. Keep the identity check, but emit a generic configured status so copied audit output does not disclose personal identity.</violation>
</file>

<file name="scripts/verify/assistant-git-boundary.sh">

<violation number="1" location="scripts/verify/assistant-git-boundary.sh:124">
P1: Assistant verification accepts legacy SSH2 private keys because the header matcher only recognizes PEM/OpenSSH-style `-----BEGIN` headers. Include SSH2 private-key headers so this audit cannot report a clean SSH boundary with such a key present.</violation>

<violation number="2" location="scripts/verify/assistant-git-boundary.sh:144">
P1: Assistant verification passes despite a persisted GitHub HTTPS token in `~/.git-credentials`. Check this standard Git credential-store file before reporting the credential boundary as clean.</violation>
</file>

<file name="docs/mise.md">

<violation number="1" location="docs/mise.md:78">
P3: In the 'Live host checks' block, the newly added `verify:bootstrap:workstation`, `verify:bootstrap:assistant`, `audit:workstation`, and `audit:workstation:json` commands now sit next to the legacy `verify:bootstrap:personal`, `audit:personal`, and `audit:personal:json` commands in the same snippet. Since `personal` is a compatibility alias that runs the same workstation script, listing both makes it look like a reader must run duplicate checks and diverges from the rest of the docs (agent-readiness.md, README, AGENTS.md), which only show the workstation variants. Consider dropping the `personal` live-check entries here (or noting they are aliases of the workstation commands) so the doc reflects the canonical profile names consistently.</violation>
</file>

<file name="scripts/lib/profile.sh">

<violation number="1" location="scripts/lib/profile.sh:22">
P3: When the profile is resolved from the profile file, the value is read verbatim and never trimmed. A manually edited ~/.config/uinaf/profile containing surrounding whitespace (trailing space, tab, or CRLF) produces a value that matches none of the case patterns in uinaf_normalize_profile, so a perfectly valid profile is silently rejected with exit code 2. Trimming the read value (which also normalizes CRLF from an editor) makes profile resolution more robust.</violation>
</file>

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Re-trigger cubic

Comment thread chezmoi/private_dot_local/private_bin/executable_git-as-github-app Outdated
Comment thread scripts/verify/assistant-git-boundary.sh Outdated
Comment thread scripts/verify/assistant-git-boundary.sh Outdated
Comment thread scripts/bootstrap/install.sh Outdated
Comment thread scripts/bootstrap/brew-bundle.sh Outdated
Comment thread scripts/audit/workstation.sh Outdated
Comment thread scripts/bootstrap/install.sh
Comment thread scripts/verify/repo.sh
Comment thread docs/mise.md
Comment thread scripts/lib/profile.sh Outdated

@cubic-dev-ai cubic-dev-ai 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.

11 issues found and verified against the latest diff

Confidence score: 2/5

  • In chezmoi/private_dot_local/private_bin/executable_uinaf-git-app, allowing a later -c credential.helper=... to override the reset undermines assistant credential isolation and can route auth through a persistent or arbitrary helper; lock down argument handling so helper overrides cannot re-enable persisted credentials.
  • scripts/verify/assistant-git-boundary.sh can miss default Git credential-store files, creating a false “clean” result while plaintext GitHub tokens remain in assistant home; expand the audit to inspect default credential-store paths and fail when found.
  • scripts/verify/bootstrap.sh currently enforces an SSH prompt expectation that does not hold for clean assistant homes, so assistant bootstrap verification can fail despite correct setup; scope the %n@%m prompt check to devbox sessions only.
  • There is meaningful regression risk from profile-related branching without full validation in scripts/bootstrap/install.sh, scripts/verify/profiles.sh, and scripts/bootstrap/brew-bundle.sh (including conflicting profile argument precedence), which can lead to wrong-role installs or untested paths; add coverage for assistant/workstation/devbox profile resolution and conflicting-arg behavior before relying on these flows.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="docs/agent-readiness.md">

<violation number="1" location="docs/agent-readiness.md:34">
P2: Custom agent: **Flag AI Slop and Fabricated Changes**

The docs assert that `mise run verify:bootstrap:workstation` proves desktop/developer CLIs, Homebrew layers, mise, Codex defaults, and installed config exist, but no backing task or script definition for this command is visible anywhere in the repository (it only appears in docs). Either add the implementation or add a TODO/coming-soon note so the docs don't overstate what exists.</violation>

<violation number="2" location="docs/agent-readiness.md:36">
P2: Custom agent: **Flag AI Slop and Fabricated Changes**

The docs strongly assert that `mise run verify:bootstrap:assistant` verifies the absence of persisted GitHub CLI, signing, or outbound SSH credentials. This exact command string only appears in documentation files after a full repo search, and there is no visible task or script backing it. Without the implementation, the docs overstate what is shipped.</violation>

<violation number="3" location="docs/agent-readiness.md:39">
P2: Custom agent: **Flag AI Slop and Fabricated Changes**

The docs claim `mise run audit:workstation` verifies human shell, Git, SSH, Codex, and local secret boundaries, but this command string only appears in docs across the repo. No visible task or script definition supports it, so the documentation overstates implemented behavior.</violation>
</file>

<file name="scripts/bootstrap/install.sh">

<violation number="1" location="scripts/bootstrap/install.sh:48">
P2: Custom agent: **Enforce Pragmatic Test Coverage**

The profile-aware rewrite of install.sh adds new user-facing branching (profile resolution, invalid-profile exit, --print-steps, developer vs. assistant setup) but there is no visible test coverage for those paths. Consider adding a lightweight verification script under scripts/verify/ that exercises the main success case (valid profile, developer path), the assistant-only path, and the invalid-profile failure, or use a bats/spec test if the project adopts one. Without coverage, regressions in bootstrap argument handling or profile gating are likely to go unnoticed.</violation>
</file>

<file name="scripts/verify/profiles.sh">

<violation number="1" location="scripts/verify/profiles.sh:49">
P2: Custom agent: **Enforce Pragmatic Test Coverage**

The profile contract test covers `devbox` only for Brewfile layer resolution, while `workstation` and `assistant` are tested across install steps, rendered mise config, managed files, applied dotfiles, and state boundaries. Since `devbox` is a first-class developer profile with the same install/render contract as `workstation`, this script should also validate that `--profile devbox` produces the correct install steps, rendered targets, and managed files so regressions in devbox-specific wiring are caught here.</violation>
</file>

<file name="scripts/bootstrap/apply-dotfiles.sh">

<violation number="1" location="scripts/bootstrap/apply-dotfiles.sh:18">
P3: Help text says `--profile` is required, but an existing profile marker or `DOTFILES_PROFILE` is accepted. Make the option optional in usage and document the fallback, matching the shared resolver.</violation>
</file>

<file name="scripts/verify/bootstrap.sh">

<violation number="1" location="scripts/verify/bootstrap.sh:291">
P1: Assistant bootstrap verification now fails its SSH-prompt check on a clean assistant home. The managed prompt only adds `%n@%m` for devbox.env-backed sessions, so limit this check to `devbox`; assistants intentionally skip that state.</violation>
</file>

<file name="scripts/bootstrap/brew-bundle.sh">

<violation number="1" location="scripts/bootstrap/brew-bundle.sh:32">
P3: Conflicting profile arguments can silently install a different role: `assistant --profile workstation` selects workstation because the `--profile` branch overwrites `profile`. Match the positional branch's duplicate-profile rejection before accepting `--profile`.</violation>
</file>

<file name="scripts/audit/workstation.sh">

<violation number="1" location="scripts/audit/workstation.sh:179">
P2: Private keys stored below `~/.ssh` are never checked, so a group/world-readable `~/.ssh/keys/id_ed25519` passes this audit. Traverse the SSH tree rather than limiting inspection to depth one.</violation>
</file>

<file name="chezmoi/private_dot_local/private_bin/executable_uinaf-git-app">

<violation number="1" location="chezmoi/private_dot_local/private_bin/executable_uinaf-git-app:32">
P1: A caller can override the credential-helper reset with a later `-c credential.helper=…` argument, so this wrapper can invoke a persistent or arbitrary helper and defeat the assistant credential-isolation guarantee. Reject credential-related config overrides (including `--config-env`) or otherwise ensure the reset is applied after caller options.</violation>
</file>

<file name="scripts/verify/assistant-git-boundary.sh">

<violation number="1" location="scripts/verify/assistant-git-boundary.sh:82">
P1: A retained Git credential-store file is not inspected, so this audit can report no persisted GitHub credentials while a plaintext GitHub token remains in the assistant home. Check the default credential-store files alongside the other user-home Git state.</violation>
</file>

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Re-trigger cubic

Comment thread scripts/verify/bootstrap.sh Outdated
Comment thread chezmoi/private_dot_local/private_bin/executable_git-as-github-app Outdated
Comment thread scripts/verify/assistant-git-boundary.sh Outdated
Comment thread docs/agent-readiness.md
Comment thread docs/agent-readiness.md Outdated
Comment thread scripts/bootstrap/install.sh Outdated
Comment thread scripts/verify/profiles.sh Outdated
Comment thread scripts/audit/workstation.sh Outdated
Comment thread scripts/bootstrap/apply-dotfiles.sh Outdated
Comment thread scripts/bootstrap/brew-bundle.sh

@cubic-dev-ai cubic-dev-ai 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.

12 issues found across 45 files

Confidence score: 2/5

  • In scripts/bootstrap/configure-git.sh and scripts/lib/profile.sh, profile selection can drift from the persisted per-user role (--profile omission and DOTFILES_PROFILE override), which can break the assistant isolation boundary and write the wrong Git policy (including signing behavior) — enforce persisted-profile precedence and require explicit profile handling in bootstrap paths.
  • scripts/verify/assistant-git-boundary.sh currently has bypass paths (GIT_CONFIG_GLOBAL override and missing checks for credential-store files like ~/.git-credentials), so verification may pass while unsafe credential/signing config is still active — validate Git’s effective global config and add credential-store checks.
  • scripts/verify/bootstrap.sh has correctness issues in profile/prompt verification (devbox-specific %n@%m assertion and raw legacy profile marker comparison), which can create false failures and reduce trust in CI signals — scope the SSH-prompt assertion to devbox and normalize legacy profile aliases before comparison.
  • In scripts/audit/workstation.sh, the audit both exposes PII (full Git name/email in logs) and misses key security states (nested ~/.ssh key permissions and explicit global Git identity/signing), so audits can leak sensitive data while giving a false sense of compliance — redact identity output, recurse SSH permission checks, and read global Git config explicitly.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/verify/bootstrap.sh">

<violation number="1" location="scripts/verify/bootstrap.sh:291">
P1: Assistant bootstrap verification now fails its SSH-prompt check: assistant dotfiles do not install `devbox.env`, which is the guard that defines the `%n@%m` prompt. Restrict this devbox-specific assertion to `devbox`.</violation>

<violation number="2" location="scripts/verify/bootstrap.sh:373">
P3: The new profile marker check compares the raw stored line in ~/.config/uinaf/profile directly against the resolved profile name, so it rejects the legacy 'personal' value even though it is explicitly supported as a compatibility alias for 'workstation'. Since the resolver (uinaf_resolve_profile) deliberately normalizes whatever is read from that file, the stored value can legitimately be 'personal', and this branch would then fail live verification even on a correctly configured machine. Consider normalizing the stored value before comparing so the verify gate stays consistent with the resolver's supported alias.</violation>
</file>

<file name="scripts/bootstrap/configure-git.sh">

<violation number="1" location="scripts/bootstrap/configure-git.sh:285">
P1: Omitting `--profile` ignores the persisted per-user role and defaults an assistant home to `workstation`, so this can write a human-profile Git config rather than the required unsigned workload config. Resolve via `uinaf_resolve_profile` before applying defaults, matching the other bootstrap entrypoints.</violation>
</file>

<file name="Brewfile">

<violation number="1" location="Brewfile:1">
P3: The repo split moves shellcheck, actionlint, gitleaks, trufflehog, and gh out of Brewfile into Brewfile.developer, but repo.sh's need_command still tells users to 'install the shared Brewfile first' for those tools. On an assistant/machine that only installs the minimal base layer, following that guidance won't provide the missing tools, so the message is now misleading. Consider updating need_command to name Brewfile.developer (or the profile layer) for tools no longer in the base.</violation>
</file>

<file name="scripts/audit/workstation.sh">

<violation number="1" location="scripts/audit/workstation.sh:40">
P3: The `personal` compatibility alias now routes `--json` output through `workstation.sh`, which reports `"audit":"workstation-security"` instead of the previous `"audit":"personal-security"`. Anyone consuming the personal audit's JSON (e.g. a remote-collection agent keyed on the audit name) would silently stop matching. If any external consumer relies on the `personal-security` identifier, consider keeping the old name when invoked via the alias, or at least call out this JSON-name change in the PR description/docs.</violation>

<violation number="2" location="scripts/audit/workstation.sh:134">
P2: Audit can report a repository-local identity/signing setting as the workstation state, leaving the global profile identity missing or disabled for new repositories. Read the global config explicitly so this checks the identity written by `configure-git.sh`.</violation>

<violation number="3" location="scripts/audit/workstation.sh:140">
P1: Custom agent: **Prevent Sensitive Data in Logs**

The workstation audit prints the user's full Git name and email address into the audit trail via the `ok` helper, exposing PII without redaction. This violates the rule that audit trails must not expose sensitive data such as emails and personal identifiers. The sibling devbox audit already avoids this by reporting only `git identity configured` without the raw values; the workstation script should do the same.</violation>

<violation number="4" location="scripts/audit/workstation.sh:179">
P2: Private keys stored below `~/.ssh` are skipped, so a configured nested `GIT_SSH_IDENTITY_FILE` can remain group/world-readable while this audit passes. Traverse the SSH tree rather than restricting the scan to depth one.</violation>
</file>

<file name="scripts/verify/assistant-git-boundary.sh">

<violation number="1" location="scripts/verify/assistant-git-boundary.sh:19">
P1: An exported `GIT_CONFIG_GLOBAL` can make normal Git use an unchecked global config with credential or signing settings while this verifier still passes. Reject the override or validate the effective global config instead of assuming `$HOME/.gitconfig` is active.</violation>

<violation number="2" location="scripts/verify/assistant-git-boundary.sh:87">
P2: Assistant verification can pass while a GitHub credential remains persisted in the user home. The added config loop checks Git config files but omits Git's credential-store locations, including `~/.git-credentials` and `$XDG_CONFIG_HOME/git/credentials`; the boundary check should reject those files (and other supported credential stores) before reporting a clean assistant home.</violation>
</file>

<file name="scripts/bootstrap/brew-bundle.sh">

<violation number="1" location="scripts/bootstrap/brew-bundle.sh:117">
P3: The installed file list is now defined twice: --print-files reads it from uinaf_profile_brewfiles, while the actual install path (run_bundle plus the shared_only guard) reconstructs the same base/developer/profile sequence by hand. If a future profile change adds a layer to profile.sh, --print-files and the real install can silently diverge. Consider having the install path iterate uinaf_profile_brewfiles too so there is a single source of truth for which Brewfiles a profile runs.</violation>
</file>

<file name="scripts/lib/profile.sh">

<violation number="1" location="scripts/lib/profile.sh:18">
P1: Custom agent: **Flag Security Vulnerabilities**

`DOTFILES_PROFILE` silently overrides the persistent per-user profile file, which undermines the assistant isolation boundary this PR introduces. The documentation says the role is stored in `~/.config/uinaf/profile`, yet `uinaf_resolve_profile` gives priority to the undocumented `DOTFILES_PROFILE` environment variable. If an assistant process inherits `DOTFILES_PROFILE=workstation`, profile-aware scripts can resolve to `workstation` and install human Git identity, signing material, or SSH credentials that should be excluded from assistant users.

Consider reading the profile file first and only falling back to `DOTFILES_PROFILE` when the file is absent, or emit a warning when the environment variable disagrees with the on-disk marker. If `DOTFILES_PROFILE` is meant as a temporary override, it should be documented and gated to avoid accidental credential exposure.</violation>
</file>

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Re-trigger cubic

Comment thread scripts/verify/bootstrap.sh Outdated
Comment thread scripts/bootstrap/configure-git.sh Outdated
Comment thread scripts/verify/assistant-git-boundary.sh
Comment thread scripts/lib/profile.sh Outdated
Comment thread scripts/audit/workstation.sh Outdated
Comment thread scripts/verify/assistant-git-boundary.sh Outdated
Comment thread Brewfile Outdated
Comment thread scripts/bootstrap/brew-bundle.sh Outdated
Comment thread scripts/audit/workstation.sh Outdated
Comment thread scripts/verify/bootstrap.sh Outdated

@cubic-dev-ai cubic-dev-ai 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.

10 issues found and verified against the latest diff

Confidence score: 2/5

  • In scripts/verify/bootstrap.sh and scripts/verify/assistant-git-boundary.sh, verification can both fail valid assistant setups and miss persisted GitHub credentials, which undermines trust in the bootstrap/boundary gates and can leave sensitive auth material in assistant homes — make the SSH prompt assertion assistant-aware and explicitly fail on nonempty credential-store files (~/.git-credentials and XDG paths).
  • scripts/audit/workstation.sh has multiple data-exposure gaps: normal runs can echo shell startup output (including accidental tokens), non-zsh login shells can bypass the token probe, and Git email is printed unredacted, so audits may leak secrets while giving false assurance — silence startup sourcing consistently, detect the actual login shell (or scope checks clearly), and redact email fields in output.
  • In scripts/bootstrap/install-git-hooks.sh, the unanchored (uinaf-)?dotfiles: pre-push grep can match user hook bodies and trigger unintended overwrite/removal behavior, creating a real risk of clobbering custom hooks — anchor the detection to the managed marker format before applying changes.
  • scripts/bootstrap/brew-bundle.sh duplicates layer-selection logic from scripts/lib/profile.sh, which risks drift in Brewfile resolution and inconsistent installs across profiles — route install selection through dotfiles_profile_brewfiles to keep one source of truth.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/bootstrap/apply-dotfiles.sh">

<violation number="1" location="scripts/bootstrap/apply-dotfiles.sh:18">
P3: `--help` says `--profile` is mandatory, but profile markers and `DOTFILES_PROFILE` are supported inputs. Show it as optional so documented invocation matches the resolver and compatibility flow.</violation>
</file>

<file name="scripts/verify/bootstrap.sh">

<violation number="1" location="scripts/verify/bootstrap.sh:297">
P1: Fresh assistant bootstrap verification always fails the SSH prompt check: assistant reaches this assertion, but the managed zsh config adds `%n@%m` only for users with `devbox.env`. Skip this check for assistant, or make the prompt configuration explicitly profile-aware.</violation>
</file>

<file name="scripts/verify/configure-git.sh">

<violation number="1" location="scripts/verify/configure-git.sh:320">
P3: The assistant identity proof fails in shells/CI that export `GIT_AUTHOR_NAME` or `GIT_AUTHOR_EMAIL`, since Git gives those variables precedence over the configured workload identity. Clear the author/committer overrides for this fixture so it verifies the generated config rather than caller state.</violation>
</file>

<file name="scripts/verify/assistant-git-boundary.sh">

<violation number="1" location="scripts/verify/assistant-git-boundary.sh:145">
P1: Persisted GitHub tokens in Git credential-store files pass this boundary check, so an assistant home can retain credentials while verification reports none. Check nonempty `~/.git-credentials` plus `$XDG_CONFIG_HOME/git/credentials` (including the `$HOME/.config` fallback) and fail like `hosts.yml`.</violation>
</file>

<file name="scripts/audit/workstation.sh">

<violation number="1" location="scripts/audit/workstation.sh:94">
P2: Users with a non-zsh login shell can pass this check while their actual login startup exports `INFISICAL_TOKEN`. Probe the configured login shell, or explicitly limit the audit and documentation to zsh users.</violation>

<violation number="2" location="scripts/audit/workstation.sh:95">
P2: Normal audit runs can relay output from `.zprofile`/`.zshrc`, including an accidentally printed token. Silence this probe as JSON mode already does so audit output does not expose startup-file output.</violation>

<violation number="3" location="scripts/audit/workstation.sh:140">
P2: Custom agent: **Prevent Sensitive Data in Logs**

The audit trail exposes the user's Git email address without redaction in the `ok` output. Rule 1 prohibits exposing emails in audit trails. The same repository already handles this safely in `scripts/audit/devbox.sh` by printing only `ok "git identity configured"` without embedding the actual name/email values. Please remove the interpolated PII from this message to avoid leaking sensitive data in audit output that may be collected or shared.</violation>

<violation number="4" location="scripts/audit/workstation.sh:168">
P3: SSH permission criteria now exist in two identical role scripts, so later key-filter or mode fixes can drift. A shared audit helper would keep this security check consistent.</violation>
</file>

<file name="scripts/bootstrap/install-git-hooks.sh">

<violation number="1" location="scripts/bootstrap/install-git-hooks.sh:23">
P2: Custom agent: **Flag Security Vulnerabilities**

The unanchored grep pattern `(uinaf-)?dotfiles: pre-push` can falsely match user-owned pre-push hooks that contain that text anywhere in their body. When that happens, the script skips the backup step and silently overwrites the existing hook, risking loss of custom security checks or logic. Other marker patterns in this repo (e.g., configure-git.sh) already anchor with `^` and `$`; this one should too.</violation>
</file>

<file name="scripts/bootstrap/brew-bundle.sh">

<violation number="1" location="scripts/bootstrap/brew-bundle.sh:116">
P2: Custom agent: **Flag AI Slop and Fabricated Changes**

The Brewfile install sequence duplicates the layer logic already defined in `dotfiles_profile_brewfiles` (`scripts/lib/profile.sh`). The install path hard-codes Brewfile → Brewfile.developer (developer check) → Brewfile.$profile, while the `--print-files` branch already iterates over that same library function. If someone updates the layer ordering or adds/removes a layer in one place, `--print-files` and the actual installs can silently drift. Consider having the install path iterate over `dotfiles_profile_brewfiles` and call `run_bundle` for each returned file, just like `--print-files` does, so the Brewfile sequence is maintained in a single place.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread scripts/verify/bootstrap.sh
Comment thread scripts/verify/assistant-git-boundary.sh Outdated
Comment thread scripts/audit/workstation.sh Outdated
Comment thread scripts/audit/workstation.sh Outdated
Comment thread scripts/bootstrap/install-git-hooks.sh Outdated
Comment thread scripts/bootstrap/brew-bundle.sh Outdated
Comment thread scripts/audit/workstation.sh Outdated
Comment thread scripts/bootstrap/apply-dotfiles.sh Outdated
Comment thread scripts/verify/configure-git.sh
Comment thread scripts/audit/workstation.sh

@cubic-dev-ai cubic-dev-ai 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.

18 issues found across 68 files

Confidence score: 2/5

  • In scripts/verify/assistant-git-boundary.sh, the boundary audit can miss persisted HTTPS tokens in Git credential-store files and PEM keys with leading blank lines, so an assistant environment may be marked clean while reusable credentials remain; this is a direct secret-isolation risk — audit both Git credential-store locations and scan readable SSH files for private-key headers regardless of leading whitespace.
  • In scripts/bootstrap/install-devbox-service-daemons.sh, ignoring legacy bootout failures before installing under a new label can leave old and new daemons running concurrently, which risks conflicting background behavior and hard-to-diagnose startup state — gate replacement on confirmed unload/loaded-state checks and fail when legacy teardown does not complete.
  • In scripts/verify/bootstrap.sh, the current assistant bootstrap check requires a devbox-only SSH prompt setting, causing clean supported assistant profiles to fail verification and block expected onboarding paths — scope that assertion to devbox so assistant validation reflects supported defaults.
  • Across scripts/audit/workstation.sh, scripts/lib/audit.sh, and scripts/verify/configure-git.sh, verification can pass or fail for the wrong reasons (repo-local Git config satisfying global checks, missing legacy audit policy fallback, and inherited env vars/tests not asserting rejection reasons), reducing trust in CI signals — force global-scope config reads, add policy-path fallback/sourcing parity, sanitize baseline env inputs, and strengthen rejection assertions.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/bootstrap/apply-dotfiles.sh">

<violation number="1" location="scripts/bootstrap/apply-dotfiles.sh:176">
P2: `mise run dotfiles:apply` and `mise run dotfiles:diff` now fail for a first-time user because both wrappers omit `--profile` and no profile marker exists yet. Update those task entry points to accept/pass a profile (or otherwise provide an explicit profile) so the documented task surface remains usable.</violation>
</file>

<file name="scripts/verify/bootstrap.sh">

<violation number="1" location="scripts/verify/bootstrap.sh:291">
P2: Assistant bootstrap verification fails on a clean supported assistant profile: it now requires a devbox-only SSH prompt that assistant dotfiles do not configure. Restrict this check to `devbox` so `verify/bootstrap.sh --profile assistant` can complete.</violation>
</file>

<file name="scripts/lib/audit.sh">

<violation number="1" location="scripts/lib/audit.sh:242">
P2: The default audit policy path moved from `~/.config/uinaf/audit.env` to `~/.config/dotfiles/audit.env`, but `load_audit_policy` has no legacy fallback. `workstation.sh`/`devbox.sh`/`host.sh` run standalone and never invoke the `apply-dotfiles.sh` migration, so a user who has not yet re-run bootstrap silently loses their existing policy (e.g. `GH_SENSITIVE_SCOPES`/`GH_ACCEPTED_SCOPES`) and the audit proceeds without it. Profile resolution in `scripts/lib/profile.sh` already keeps a `~/.config/uinaf/profile` fallback for this reason; mirror that here.</violation>
</file>

<file name="scripts/verify/vendor-neutral.sh">

<violation number="1" location="scripts/verify/vendor-neutral.sh:55">
P2: Vendor-neutral verification reports success when `git grep` fails (for example, a repository/index/read error), because `|| true` erases its status. Preserve exit status 1 as the no-match case but fail for other statuses so this gate cannot silently skip its scan.</violation>
</file>

<file name="scripts/verify/configure-git.sh">

<violation number="1" location="scripts/verify/configure-git.sh:301">
P2: Assistant verification inherits signing/SSH setup variables from its caller, so it fails before assertions when a developer or CI environment exports any of these values. Clear these inputs for the baseline assistant setup so this fixture stays deterministic.</violation>

<violation number="2" location="scripts/verify/configure-git.sh:326">
P2: Custom agent: **Enforce Pragmatic Test Coverage**

The assistant rejection tests only verify a non-zero exit and a single-file checksum, but they do not assert the actual rejection reason and would still pass if the assistant-specific restrictions were removed.

The tests pass `GIT_SIGN_COMMITS=true GIT_SIGNING_KEY=\$assistant_home/signing` and `GIT_SSH_IDENTITY_FILE=\$assistant_home/github-key` to paths that were never created. Because `configure-git.sh` exits early with assistant-specific messages ("assistant workload commits do not use a persisted signing key" / "assistant GitHub authentication uses an installation token over HTTPS, not an SSH identity file") before reaching `validate_local_ssh_private_key`, the missing files are not the actual reason for failure. However, the tests swallow stdout/stderr and do not grep for those messages. If the assistant guard were accidentally removed, the generic "key file does not exist" validation would still produce a non-zero exit and leave `.gitconfig.local` unchanged, so these tests would silently pass for the wrong reason.

Consider capturing stderr and asserting the expected rejection message (as `assert_rejected_without_mutation` does elsewhere), and consider snapshotting the other owned paths to detect unintended mutations.</violation>
</file>

<file name="scripts/bootstrap/install-devbox-service-daemons.sh">

<violation number="1" location="scripts/bootstrap/install-devbox-service-daemons.sh:284">
P1: A failed legacy unload can leave two daemons running: this ignores `bootout` errors, moves the old plist aside, then installs the replacement under a different label. Check whether the legacy job is loaded and require a successful `bootout` before retiring its plist; only tolerate the known not-loaded case.</violation>
</file>

<file name="scripts/lib/launchd.sh">

<violation number="1" location="scripts/lib/launchd.sh:16">
P3: Missing `service` or `user` aborts under `set -u` before the validation cases run, so callers cannot handle malformed input via the documented return path. Default both positional expansions to empty so the existing cases return 2.</violation>
</file>

<file name="scripts/verify/service-labels.sh">

<violation number="1" location="scripts/verify/service-labels.sh:17">
P3: The default-label assertion is fragile: it relies on the caller's environment having no DOTFILES_LAUNCHD_NAMESPACE set. If a dev/CI export sets that variable (it's the documented override for the installer), the installer returns org.example.* labels and this deterministic repo gate fails spuriously even though behavior is correct. Pin the default by running the installer with the variable cleared (e.g. DOTFILES_LAUNCHD_NAMESPACE=) so the gate tests the actual default path.</violation>
</file>

<file name="scripts/audit/workstation.sh">

<violation number="1" location="scripts/audit/workstation.sh:134">
P2: Running the documented audit from this repository can let repository-local Git settings satisfy these checks while the user's managed identity/signing settings are missing or disabled. Query the global config with includes so the audit reflects the per-user state it claims to verify.</violation>

<violation number="2" location="scripts/audit/workstation.sh:140">
P2: Custom agent: **Prevent Sensitive Data in Logs**

The audit script outputs the user's Git email address in plaintext through the `ok` helper, which writes to stdout as part of the audit trail. Since email addresses are PII, they should be redacted or omitted from audit output to avoid exposing sensitive data if the output is saved, shared, or collected. Consider masking the email (e.g., `<***>`) or removing it from the message while still confirming that an identity is configured.</violation>

<violation number="3" location="scripts/audit/workstation.sh:171">
P3: This repeats the complete SSH-key permission rule already in `scripts/audit/devbox.sh`, so fixes and policy changes can drift between the two profile audits. A shared helper in `scripts/lib/audit.sh` would keep the invariant in one place.</violation>

<violation number="4" location="scripts/audit/workstation.sh:179">
P2: Workstations with a readable `~/.ssh/authorized_keys` will fail this private-key check even though that file contains public authorization keys. Limit the scan to recognized private-key files, or at least exclude non-private SSH files such as `authorized_keys`, before applying the confidentiality mode rule.</violation>
</file>

<file name="scripts/verify/assistant-git-boundary.sh">

<violation number="1" location="scripts/verify/assistant-git-boundary.sh:122">
P1: A private key with a leading blank line is not detected, so the audit reports a clean assistant home while a usable PEM key remains under `~/.ssh`. Search each readable SSH file for the private-key header rather than only its first line.</violation>

<violation number="2" location="scripts/verify/assistant-git-boundary.sh:135">
P1: Persisted GitHub tokens in Git credential-store files pass this boundary check. Audit `$HOME/.git-credentials` and the XDG Git credentials file so an assistant home retaining a prior HTTPS credential is rejected.</violation>
</file>

<file name="docs/bootstrap.md">

<violation number="1" location="docs/bootstrap.md:362">
P3: The Assistant User flow bounces from "Start with a dedicated Unix user and a clean home" straight into `./scripts/bootstrap/install.sh` without first fetching the repository into that fresh home. Every other profile depends on the repo checkout (the guide opens with "Run commands from the repo root"), so an assistant or agent following this literally has no repo checkout to run these scripts from. Consider adding an explicit clone step (as admin or under the assistant identity) before the user-local setup, or a pointer to the clone instructions above.</violation>
</file>

<file name="scripts/secrets/configure-infisical-devbox.sh">

<violation number="1" location="scripts/secrets/configure-infisical-devbox.sh:5">
P2: After these path renames, the standalone Infisical/sudo/devbox secrets scripts read config only from `~/.config/dotfiles/`, but nothing in these scripts migrates an existing `~/.config/uinaf/devbox.env` / `infisical-machine.env` / `sudo-age-identity.txt`. The migration exists only in `apply-dotfiles.sh` (`migrate_legacy_config`). So on an existing devbox that upgrades the repo and runs one of these secrets scripts directly without first re-running `apply-dotfiles`/`install.sh`, the existing machine config and age identity will be silently invisible, forcing a reconfigure (or a failed sudo run). Consider having the secrets scripts fall back to the legacy path (matching how `scripts/lib/profile.sh` falls back to `~/.config/uinaf/profile`) or documenting that apply must run before these scripts on upgraded machines.</violation>
</file>

<file name="scripts/verify/devbox-services.sh">

<violation number="1" location="scripts/verify/devbox-services.sh:239">
P2: The verification scans for managed healthd/colima system daemons using only `dotfiles_resolve_launchd_namespace`, which resolves the namespace from `DOTFILES_LAUNCHD_NAMESPACE` (or the default `local.dotfiles`). However, `install-devbox-service-daemons.sh` accepts the namespace independently via `--namespace` at install time (also populated from `DOTFILES_LAUNCHD_NAMESPACE`). If an operator installs the daemons with `--namespace org.example.dotfiles` without exporting the matching environment variable, this check will scan the default `local.dotfiles.*` globs and the legacy `com.uinaf.*` globs only, find nothing, and print 'ok no managed healthd/colima system daemons on this machine' — a false success that masks live jobs under the custom namespace. Consider reading the namespace from the same source the installer used (for example, persisting it to the config file the installer consults, or checking both customized and default namespaces) so verification and install cannot drift.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread scripts/bootstrap/install-devbox-service-daemons.sh Outdated
Comment thread scripts/verify/assistant-git-boundary.sh Outdated
Comment thread scripts/verify/assistant-git-boundary.sh Outdated
Comment thread scripts/bootstrap/apply-dotfiles.sh
Comment thread scripts/verify/bootstrap.sh Outdated
Comment thread scripts/audit/workstation.sh Outdated
Comment thread scripts/lib/launchd.sh Outdated
Comment thread scripts/verify/service-labels.sh Outdated
Comment thread scripts/audit/workstation.sh Outdated
Comment thread docs/bootstrap.md

@cubic-dev-ai cubic-dev-ai 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.

19 issues found across 71 files

Confidence score: 2/5

  • In scripts/audit/workstation.sh, the default prose-mode scanner can emit matched secret material into logs, which turns the audit trail itself into a data-exposure risk—switch to redacted output by default (or require an explicit opt-in for prose output).
  • In scripts/bootstrap/install-devbox-service-daemons.sh, unloading/moving the legacy daemon before install_job succeeds can leave previously running services stopped after a failed replacement, and --check can now fail across users due to owner-only contract reads—add rollback/restore of the legacy job on failure and relax check-mode’s permission boundary.
  • The new config-path resolution behavior across scripts/secrets/infisical-devbox-run.sh, scripts/secrets/infisical-devbox-sudo-seal.sh, and scripts/audit/devbox.sh is inconsistent (new resolver branches vs hardcoded paths), creating risk of selecting the wrong config/identity and missing security checks—centralize all consumers on dotfiles_resolve_config_file and verify each branch explicitly.
  • Coverage gaps in .mise/tasks/verify/bootstrap/personal and the updated audit/resolver paths mean key branches (--profile personal, resolver fallbacks, and new devbox pass/fail checks) may regress silently—add focused regression tests for those changed paths before relying on this behavior.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".mise/tasks/verify/bootstrap/personal">

<violation number="1" location=".mise/tasks/verify/bootstrap/personal:5">
P2: Custom agent: **Enforce Pragmatic Test Coverage**

The `personal` verification task no longer exercises the `--profile personal` path in `scripts/verify/bootstrap.sh`, even though the PR claims to retain `personal` as a compatibility alias. Since this task was the only automated caller using `--profile personal`, the bootstrap script’s CLI handling for the legacy alias now has no end-to-end regression coverage. Consider keeping `--profile personal` in this alias task so that a breakage in `bootstrap.sh`’s argument parsing or alias resolution is still caught by the verification suite.</violation>
</file>

<file name="scripts/secrets/infisical-devbox-run.sh">

<violation number="1" location="scripts/secrets/infisical-devbox-run.sh:9">
P2: Custom agent: **Enforce Pragmatic Test Coverage**

The new shared `dotfiles_resolve_config_file` resolver introduces multiple branches (explicit env-var override, canonical path, legacy fallback, default) that determine where security-critical credential configs are loaded from. None of these branches are exercised by tests, and the repository contains no tests for this new shared resolver or for the script's failure paths (missing file, wrong mode). The PR should add tests covering the main success and failure paths of `dotfiles_resolve_config_file` and the updated config loading in this script.</violation>
</file>

<file name="scripts/README.md">

<violation number="1" location="scripts/README.md:52">
P2: Custom agent: **Flag AI Slop and Fabricated Changes**

The assistant bootstrap example uses literal placeholder strings (`'Workload Name'` and `'APP_BOT_NOREPLY_EMAIL'`) without any substitution guidance. Because every other example in the block uses real, runnable values, a reader is likely to copy-paste this command literally, which would configure Git with an invalid email address. Replace the literals with clearly marked placeholders (e.g., `<your-workload-name>` and `<your-bot-noreply-email>`) or add an explicit note that these values must be substituted.</violation>
</file>

<file name="scripts/verify/devbox-services.sh">

<violation number="1" location="scripts/verify/devbox-services.sh:244">
P3: Custom agent: **Flag AI Slop and Fabricated Changes**

The empty `then` branch and indirect status capture (`if cmd; then :; else status=$?; fi`) is more verbose than necessary and inconsistent with the rest of this repository, which uses the `|| { ... }` idiom extensively for this exact pattern. This reads as patchwork control flow that should be simplified for clarity and consistency with the codebase style.</violation>
</file>

<file name="scripts/secrets/infisical-devbox-sudo-seal.sh">

<violation number="1" location="scripts/secrets/infisical-devbox-sudo-seal.sh:9">
P2: Custom agent: **Enforce Pragmatic Test Coverage**

The new `dotfiles_resolve_config_file` path resolver in the sudo seal script is security-critical—it determines which config and age identity are used when sealing the sudo password—but there is no visible test or verify coverage for its multiple resolution branches (explicit override, canonical path, legacy fallback). The repository already uses bash verify scripts for related Infisical components, so adding at least a minimal verify script for the resolver and its integration with the seal script would close this gap.</violation>
</file>

<file name="scripts/audit/devbox.sh">

<violation number="1" location="scripts/audit/devbox.sh:4">
P2: The devbox audit now hardcodes the new `~/.config/dotfiles/...` config paths instead of resolving through the shared `dotfiles_resolve_config_file` helper, which every other config consumer (devbox-services.sh, infisical-devbox-run.sh, etc.) uses with a legacy `~/.config/uinaf/...` fallback. On an upgraded machine where the legacy `~/.config/uinaf/devbox.env` still exists and the new path has not been migrated, this audit will silently fall back to defaults (just warning `missing optional ...`) and can report drift against the wrong process-compose port/socket and devbox-user settings instead of the real configuration. Consider resolving `config_path` and `machine_config_path` with `dotfiles_resolve_config_file` for the same conflict-aware legacy fallback the rest of the codebase uses.</violation>

<violation number="2" location="scripts/audit/devbox.sh:339">
P2: Custom agent: **Enforce Pragmatic Test Coverage**

The devbox audit changed two pass/fail checks, but there’s no regression coverage for the new devbox identity resolution or project-directory privacy paths. Adding a focused fixture here would help catch false positives/negatives from the audit gate.</violation>
</file>

<file name="scripts/audit/workstation.sh">

<violation number="1" location="scripts/audit/workstation.sh:40">
P3: `--json` can emit invalid JSON when `DOTFILES_AUDIT_NAME` contains a control character. Restrict this compatibility label to known values or make `json_string` escape JSON control characters before serializing environment-derived text.</violation>

<violation number="2" location="scripts/audit/workstation.sh:49">
P3: 1Password references in AWS, Docker, and shell-session files are silently missed, despite being included in the local secret-scan surface. Include regular files from those directories in `emit_workstation_reference_scan_files` so the documented warning covers the same local boundary.</violation>

<violation number="3" location="scripts/audit/workstation.sh:124">
P1: Custom agent: **Prevent Sensitive Data in Logs**

The audit script's default (prose) mode may leak matched secret material into the audit trail. The usage text itself warns users to "Treat prose scanner output as sensitive because maintained scanners can include matched secret material when they detect a leak." While `gitleaks` is invoked with `--redact`, the `trufflehog` prose-mode invocation lacks explicit redaction, and its stdout flows directly into the audit output. Since this is an audit trail, the rule against exposing sensitive data without redaction applies here. Consider adding a mask/redact flag for trufflehog (e.g., `--mask`) or parsing JSON output internally and redacting matched content before emitting it.</violation>
</file>

<file name="scripts/verify/bootstrap.sh">

<violation number="1" location="scripts/verify/bootstrap.sh:374">
P2: Valid profile markers with surrounding whitespace pass resolution but fail later config verification because this second normalization skips trimming. Trim the marker before normalizing so the selected and installed profile use the same parsing rules.</violation>
</file>

<file name="scripts/lib/profile.sh">

<violation number="1" location="scripts/lib/profile.sh:45">
P2: A valid profile file without a trailing newline is rejected, so bootstrap/verification cannot resolve a manually or externally written `assistant`/`devbox` marker. Accept `read`'s EOF result when it populated `requested`, while retaining the unreadable/empty-file failure.</violation>
</file>

<file name="Brewfile.developer">

<violation number="1" location="Brewfile.developer:10">
P2: google-chrome is role-specific (a browser, already declared in Brewfile.assistant's browser/media layer and mirrored by firefox/brave in Brewfile.workstation), so it doesn't belong in the shared coding-stack layer that only workstation and devbox consume. The documented boundary (AGENTS.md: shared coding stack only; role-specific software lives in the profile Brewfiles) suggests keeping browsers out of Brewfile.developer. Consider dropping this cask here if the intent is a pure developer layer.</violation>
</file>

<file name="scripts/verify/vendor-neutral.sh">

<violation number="1" location="scripts/verify/vendor-neutral.sh:34">
P3: The wildcard exemptions allow additional non-legacy owner branding on any line that also mentions a legacy identifier, weakening the vendor-neutral gate. Restrict exemptions to the specific documented migration statements or validate the legacy identifiers separately.</violation>
</file>

<file name="scripts/lib/audit.sh">

<violation number="1" location="scripts/lib/audit.sh:123">
P2: SSH private keys reached through symlinks are skipped, so this audit passes even when a symlinked key target is group/world-readable. Follow SSH-tree symlinks while enumerating files so mode checks cover identities referenced from `~/.ssh`.</violation>
</file>

<file name="scripts/bootstrap/install-devbox-service-daemons.sh">

<violation number="1" location="scripts/bootstrap/install-devbox-service-daemons.sh:113">
P2: `--check` from another non-root account now fails before checking launchd once a namespace contract exists, because it must read the target user's owner-only contract file. Resolve this check-mode permission boundary explicitly so callers still receive the documented launchd-level checks and functional-check skip.</violation>

<violation number="2" location="scripts/bootstrap/install-devbox-service-daemons.sh:356">
P1: A replacement-install failure leaves this previously running service stopped, because the legacy daemon is unloaded and moved before `install_job` has loaded the new job. Preserve or restore the legacy plist/job on replacement failure so migration does not turn a transient install error into an outage.</violation>
</file>

<file name="scripts/verify/assistant-git-boundary.sh">

<violation number="1" location="scripts/verify/assistant-git-boundary.sh:145">
P2: The assistant SSH-boundary audit only inspects regular files, directories, and symlinks under ~/.ssh. A socket or FIFO entry (for example a leftover SSH ControlMaster control socket) is silently skipped rather than rejected, so the 'no persisted SSH credentials' boundary can pass while such state remains under the assistant home. Consider adding a final `else` branch that fails (or at least reports) any unrecognized non-regular file type in the SSH tree, so the audit is complete and future SSH feature additions can't slip through.</violation>
</file>

<file name="scripts/lib/launchd.sh">

<violation number="1" location="scripts/lib/launchd.sh:7">
P3: LaunchDaemon namespaces containing underscores are rejected even though the installer reports underscores as valid. This makes the advertised namespace contract inconsistent and prevents organizations using such a namespace from installing or checking services; either allow `_` in the resolver or remove it from the documented error.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread scripts/audit/workstation.sh
Comment thread scripts/bootstrap/install-devbox-service-daemons.sh Outdated
Comment thread .mise/tasks/verify/bootstrap/personal
Comment thread scripts/secrets/infisical-devbox-run.sh
Comment thread scripts/README.md
Comment thread scripts/verify/devbox-services.sh Outdated
Comment thread scripts/verify/vendor-neutral.sh
Comment thread scripts/audit/workstation.sh
Comment thread scripts/audit/workstation.sh
Comment thread scripts/lib/launchd.sh Outdated

@cubic-dev-ai cubic-dev-ai 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.

13 issues found across 71 files

Confidence score: 2/5

  • The riskiest regression is in scripts/bootstrap/install-devbox-service-daemons.sh: a partial multi-service install can lose the namespace contract and create duplicate daemons on retry, which can leave launchd state inconsistent and hard to recover — persist the resolved namespace after the first successful job (or before installs begin).
  • Audit correctness is at risk across scripts/audit/devbox.sh and .mise/tasks/audit/personal/_default: legacy devboxes may be evaluated against defaults instead of devbox.env, and the personal alias drops DOTFILES_AUDIT_NAME=personal-security, so checks can be skipped/misdirected and JSON output naming can silently drift — restore wrapper/env propagation and load legacy config before running checks.
  • There are concrete security gaps in scripts/lib/audit.sh and scripts/lib/launchd.sh: symlinked SSH private keys can evade permission checks, and root service install can hang while reading a user-controlled namespace path — follow symlinks for mode_of validation and require a regular-file contract path before sed while preserving dangling-symlink failure behavior.
  • Policy/verification guardrails have bypass and signal-loss risk in scripts/verify/vendor-neutral.sh and scripts/verify/bootstrap.sh (with scripts/lib/profile.sh interaction): owner-name branding can slip through when an allowed coordinate appears on the same line, and profile resolution failures are flattened into generic usage output, reducing diagnosability — tighten full-line/occurrence validation and preserve distinct profile error codes/messages.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/audit/personal.sh">

<violation number="1" location="scripts/audit/personal.sh:6">
P2: Custom agent: **Enforce Pragmatic Test Coverage**

The `personal.sh` audit script was reduced to a one-line `exec` wrapper around `workstation.sh`, but there is no test coverage validating this delegation. The change alters observable behavior—argument forwarding, exit-code preservation, audit naming via `DOTFILES_AUDIT_NAME`, and which checks run—yet the repository contains no tests referencing this script or the environment variable it sets. Under Rule 4, changes to important behavior should cover the main success and failure paths. Consider adding a focused test or contract verification that exercises the `personal.sh` alias and asserts correct propagation.</violation>
</file>

<file name=".mise/tasks/audit/personal/_default">

<violation number="1" location=".mise/tasks/audit/personal/_default:5">
P2: The personal compatibility alias now delegates to workstation.sh directly, losing the DOTFILES_AUDIT_NAME=personal-security that the personal.sh wrapper sets. As a result the audit JSON name changes from "personal-security" to "workstation-security", diverging from the sibling .mise/tasks/audit/personal/json task (which still calls personal.sh) and silently breaking downstream consumers keyed on the audit name. Point this task at ./scripts/audit/personal.sh (or set DOTFILES_AUDIT_NAME=personal-security) to preserve the alias's identity consistently.</violation>
</file>

<file name="scripts/verify/vendor-neutral.sh">

<violation number="1" location="scripts/verify/vendor-neutral.sh:29">
P2: Branding added on a line that also contains an allowed external coordinate bypasses this gate. Match the complete expected command/URL or validate every owner-name occurrence on the line so only the bounded coordinate is exempt.</violation>
</file>

<file name="scripts/audit/devbox.sh">

<violation number="1" location="scripts/audit/devbox.sh:4">
P2: Existing devboxes retaining the recoverable legacy config are audited with defaults instead of their actual `devbox.env`, so configured process-compose and Infisical checks can be skipped or target the wrong values. Resolve this path through `dotfiles_resolve_config_file`, matching the other devbox entry points.</violation>

<violation number="2" location="scripts/audit/devbox.sh:326">
P2: The "project directory privacy" check now inspects the parent `$HOME/projects` directory itself, replacing the previous per-user child path `$HOME/projects/uinaf`. On a standard devbox layout `$HOME/projects` is created with the default umask (typically 755), so `mode_of` returns a mode whose `0077` bits are non-zero and every devbox user would start seeing a spurious "readable by group or other users" warning — pushing the audit to "warn"/non-pass in `--json` mode even when the actual per-user project dirs are correctly private. Consider dropping the parent path from the privacy check (or only warning on it) and keeping the authoritative per-user `$HOME/projects/$devbox_user` check, since the parent already appears as a trusted project root elsewhere in this script.</violation>
</file>

<file name="scripts/lib/audit.sh">

<violation number="1" location="scripts/lib/audit.sh:123">
P2: SSH private keys referenced through symlinks are skipped, so an externally readable target can pass this permission audit. Follow symlinks while enumerating files so `mode_of` checks the resolved key target.</violation>
</file>

<file name="scripts/lib/launchd.sh">

<violation number="1" location="scripts/lib/launchd.sh:27">
P2: Root service installation can hang while reading a user-controlled namespace path. Restrict the stored contract to a regular file before invoking `sed`, while retaining the explicit dangling-symlink failure behavior.</violation>
</file>

<file name="scripts/bootstrap/configure-git.sh">

<violation number="1" location="scripts/bootstrap/configure-git.sh:278">
P3: Invalid `--profile` input is reported as an empty value, making the failure harder to diagnose. Preserve the requested value while normalizing so the diagnostic identifies the unsupported profile.</violation>
</file>

<file name="scripts/bootstrap/install-devbox-service-daemons.sh">

<violation number="1" location="scripts/bootstrap/install-devbox-service-daemons.sh:91">
P3: The `--print-labels` path resolves the namespace through `dotfiles_resolve_launchd_namespace` only, which does not read the persisted per-host contract file (`~/.config/dotfiles/launchd-namespace`) that the actual install path honors via `dotfiles_resolve_launchd_namespace_contract`. On any host where a stored contract already exists (created by a prior install) and the operator runs `--print-labels` without an explicit `--namespace`, the printed labels use the default `local.dotfiles.*` namespace and will not match the labels an install would actually apply (the stored namespace). Operators scripting label lookup can therefore get misleading output that disagrees with what `install` uses. Consider having `--print-labels` consult the stored contract file when it exists (consistent with the install path) or documenting that it always prints the default namespace.</violation>

<violation number="2" location="scripts/bootstrap/install-devbox-service-daemons.sh:92">
P3: `dotfiles_resolve_launchd_namespace_contract` returns distinct error codes (2 for an invalid/empty namespace string, 4 for a contract file that exists but is not readable), but the installer collapses every non-3 status into a single "must contain dot-separated letters..." message. In check mode run as a non-root, non-target user (the script explicitly supports `--check` without being root/target via `can_run_as_target`), the 0600-owned stored namespace file may be unreadable, and the operator would be told the namespace format is invalid when the real cause is a permission/read failure. Consider distinguishing the unreadable-file case (and reporting the actual stored-namespace parse failure) so the failure message matches the true cause.</violation>

<violation number="3" location="scripts/bootstrap/install-devbox-service-daemons.sh:420">
P1: A partial multi-service install can lose its namespace contract and create duplicate daemons on retry. Persist the resolved namespace before installing the first job (or otherwise preserve it after any successful job), so a later failure cannot permit a different namespace.</violation>
</file>

<file name="scripts/verify/bootstrap.sh">

<violation number="1" location="scripts/verify/bootstrap.sh:51">
P3: When bootstrap verification can't resolve a profile, the distinct failure causes are all flattened into printing the usage text and exiting. Because scripts/lib/profile.sh distinguishes 'no profile' (1), 'invalid profile' (2), and 'unreadable stored profile' (3), the live check should surface those states instead of showing the usage block. On a machine with a corrupt or unreadable ~/.config/dotfiles/profile the current message is misleading and unactionable.</violation>
</file>

<file name="scripts/lib/profile.sh">

<violation number="1" location="scripts/lib/profile.sh:45">
P3: A stored profile file whose last line is not newline-terminated (for example one written with `printf` without a trailing `\n`, `echo -n`, or saved by an editor that omits the final newline) will be read correctly by `read` but `read` returns a non-zero exit status in that case, so the `|| return 3` branch fires and `dotfiles_resolve_profile` reports the profile as unreadable/invalid even though the value was successfully captured. Because `install.sh`, `apply-dotfiles.sh`, and `configure-git.sh` all treat status 3 as a fatal invalid-profile error, such a file would block bootstrap entirely. Guarding on emptiness rather than the read exit code handles both cases correctly.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread scripts/bootstrap/install-devbox-service-daemons.sh Outdated
Comment thread scripts/audit/personal.sh
Comment thread .mise/tasks/audit/personal/_default Outdated
Comment thread scripts/verify/vendor-neutral.sh
Comment thread scripts/audit/devbox.sh Outdated
Comment thread scripts/bootstrap/configure-git.sh
Comment thread scripts/verify/bootstrap.sh
Comment thread scripts/lib/profile.sh Outdated
Comment thread scripts/bootstrap/install-devbox-service-daemons.sh
Comment thread scripts/bootstrap/install-devbox-service-daemons.sh Outdated

@cubic-dev-ai cubic-dev-ai 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.

19 issues found across 71 files

Confidence score: 2/5

  • In scripts/audit/workstation.sh, scan_files_for_secrets can emit matched secret material in default output, which turns a protective audit into a potential credential exposure path in logs/CI artifacts — run trufflehog filesystem in a redacted/non-secret-printing mode (or suppress match bodies) before merge.
  • In scripts/lib/config-paths.sh, accepting and sourcing legacy symlinked config undermines the intended fail-closed migration boundary, so a symlink can bypass policy and load unintended settings — reject legacy symlinks at path resolution so helpers only consume canonical non-symlink config.
  • In scripts/lib/audit.sh, SSH key checks skip symlinked ~/.ssh paths, allowing weak-permission private keys behind symlinks to pass audit and reducing hardening effectiveness — follow symlink targets during key enumeration and validate target permissions.
  • Config-resolution and contract handling changed across scripts/audit/devbox.sh, scripts/lib/config-paths.sh, and scripts/lib/launchd.sh, with gaps in branch/reason assertions (scripts/verify/configure-git.sh) and behavior regressions (legacy config ignored, malformed namespace files accepted, reinstall namespace collisions), which raises regression risk in real user environments — add targeted tests for each branch/error reason and tighten namespace/config validation rules.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/audit/devbox.sh">

<violation number="1" location="scripts/audit/devbox.sh:4">
P2: Devbox audits on an unmigrated user now ignore both legacy config files, reporting missing config and using defaults instead of that user's configured contract. Resolve these paths through `dotfiles_resolve_config_file` after loading the audit library so the documented legacy fallback remains available until dotfiles are applied.</violation>

<violation number="2" location="scripts/audit/devbox.sh:339">
P2: Custom agent: **Enforce Pragmatic Test Coverage**

The Git identity checks in scripts/audit/devbox.sh were re-scoped from the effective git config to only $HOME/.gitconfig using --file and --includes. This changes what the audit validates: repo-local overrides no longer pass, and a missing ~/.gitconfig now fails hard. Core verification behavior like this should have test coverage for its success and failure paths under Rule 4. Consider adding tests that exercise the new scoped-lookup semantics in the audit script itself (e.g., a fixture with ~/.gitconfig containing the required values passes, a fixture missing ~/.gitconfig fails, and a fixture where the values exist only in a repo-local .git/config fails).</violation>
</file>

<file name="scripts/lib/config-paths.sh">

<violation number="1" location="scripts/lib/config-paths.sh:3">
P2: Custom agent: **Enforce Pragmatic Test Coverage**

The new `dotfiles_resolve_config_path` / `dotfiles_resolve_config_file` helpers introduce four branches (explicit override, canonical precedence, legacy fallback, default canonical), but only the canonical-precedence and legacy-fallback branches are exercised in `scripts/verify/profiles.sh`. The explicit-path override and default-canonical (fresh-install) branches are untested. Because environment-based overrides (e.g. `DEVBOX_CONFIG`, `INFISICAL_MACHINE_CONFIG`) rely on the explicit override branch, a regression there would silently break profile-aware configuration across the PR. Please add coverage for the explicit override and the default-canonical fallback.</violation>

<violation number="2" location="scripts/lib/config-paths.sh:12">
P1: Legacy symlinked config is accepted and then sourced by secret/verification helpers, so a legacy symlink bypasses the documented fail-closed migration boundary. Reject legacy symlinks here instead of treating them as a fallback config file.</violation>
</file>

<file name="scripts/verify/configure-git.sh">

<violation number="1" location="scripts/verify/configure-git.sh:318">
P3: Assistant verification can fail under a caller environment containing developer Git variables: inherited `GIT_SIGN_COMMITS=true`, `GIT_SIGNING_KEY`, or `GIT_SSH_IDENTITY_FILE` makes the setup reject before assertions run. Clear these variables for both successful assistant `configure-git.sh` invocations so this fixture tests the assistant profile rather than runner state.</violation>

<violation number="2" location="scripts/verify/configure-git.sh:353">
P2: Custom agent: **Enforce Pragmatic Test Coverage**

These assistant rejection tests only assert non-zero exit and checksum stability, without verifying the actual rejection reason. The rest of this test file uses `assert_rejected_without_mutation`, which captures stderr and `grep`s for the expected error message. Following that pattern here would ensure these tests actually validate the assistant-profile contract rather than any generic failure path.</violation>
</file>

<file name="scripts/audit/workstation.sh">

<violation number="1" location="scripts/audit/workstation.sh:40">
P3: `--json` can emit invalid or structurally altered JSON when `DOTFILES_AUDIT_NAME` contains a newline or another JSON control character. Escape all JSON control characters in `json_string` (or strictly validate this configurable audit name) before using it in the remote-collection output.</violation>

<violation number="2" location="scripts/audit/workstation.sh:124">
P1: Custom agent: **Prevent Sensitive Data in Logs**

The workstation audit script’s default (prose) output can emit matched secret material because `scan_files_for_secrets` runs `trufflehog filesystem` without suppressing or redacting its output. The script’s own usage text acknowledges this risk, stating that prose output should be treated as sensitive. Audit trails must not expose secrets without redaction. Consider suppressing or masking scanner output in prose mode—similar to how it is already silenced in `--json` mode—so only the script’s own sanitized pass/fail summaries are emitted.</violation>
</file>

<file name="scripts/lib/profile.sh">

<violation number="1" location="scripts/lib/profile.sh:45">
P2: A profile marker without a final newline is rejected even though its role is valid. Accept `read`'s assigned final line, while retaining the unreadable/empty-file failure path.</violation>
</file>

<file name="scripts/lib/launchd.sh">

<violation number="1" location="scripts/lib/launchd.sh:27">
P2: Malformed persisted namespace contracts with extra lines are accepted because only the first line is parsed. Validate the file has exactly one namespace line so invalid or conflicting state fails closed.</violation>
</file>

<file name="scripts/verify/vendor-neutral.sh">

<violation number="1" location="scripts/verify/vendor-neutral.sh:54">
P3: New owner-branded content in these Git scripts can bypass this check whenever it contains both words in this order. Narrow the exception to the legacy `uinaf-dotfiles` marker used by the existing compatibility code.</violation>
</file>

<file name="scripts/bootstrap/install-devbox-service-daemons.sh">

<violation number="1" location="scripts/bootstrap/install-devbox-service-daemons.sh:91">
P2: The --print-labels path resolves the namespace with dotfiles_resolve_launchd_namespace, which only considers the DOTFILES_LAUNCHD_NAMESPACE env var (falling back to local.dotfiles) and never reads the stored host contract file (~/.config/dotfiles/launchd-namespace). The real install path uses dotfiles_resolve_launchd_namespace_contract, which prefers the stored namespace file. As a result, on a host that already has a persisted namespace (e.g. org.example.dotfiles), running --print-labels without --namespace or the env var prints local.dotfiles.* labels that do not match the labels the same command would actually install, so operators/CI using --print-labels to derive or verify labels can get the wrong set.</violation>

<violation number="2" location="scripts/bootstrap/install-devbox-service-daemons.sh:347">
P2: Reinstalling a service configured with the accepted `com.uinaf` namespace always fails because this legacy check matches the service's own current label. Reject that reserved namespace during namespace validation, or skip this legacy check when old and replacement labels are identical.</violation>
</file>

<file name="scripts/bootstrap/install.sh">

<violation number="1" location="scripts/bootstrap/install.sh:31">
P2: An explicit empty or conflicting `--profile` can silently bootstrap a different stored role instead of failing. Reject empty and duplicate profile arguments before resolving the profile.</violation>
</file>

<file name="scripts/lib/audit.sh">

<violation number="1" location="scripts/lib/audit.sh:123">
P2: SSH keys reachable through a symlinked `~/.ssh` directory or key file are skipped, so a group/world-readable private key can pass this audit. Follow symlinks while enumerating the SSH tree so target-key permissions are checked.</violation>
</file>

<file name="scripts/verify/profiles.sh">

<violation number="1" location="scripts/verify/profiles.sh:52">
P3: Profile-contract verification is not isolated from `DOTFILES_PROFILE_FILE`; an exported override makes this fixture resolve a host-selected profile path and fail before checking its temporary profile. Clear that override for the profile-resolution fixture (and its subsequent missing/invalid cases).</violation>
</file>

<file name="scripts/verify/assistant-git-boundary.sh">

<violation number="1" location="scripts/verify/assistant-git-boundary.sh:9">
P3: The new `mode_of()` function duplicates the existing `file_mode()` helper already defined in `scripts/verify/brew-devbox.sh` (both are the same macOS/Linux `stat` fallback wrapper). Since both verify scripts are changed in this batch, consider lifting this into a shared `scripts/lib` helper and sourcing it from each, so the stat fallback logic is maintained in one place rather than copied.</violation>
</file>

<file name="docs/bootstrap.md">

<violation number="1" location="docs/bootstrap.md:367">
P3: The assistant setup calls `./scripts/secrets/configure-infisical-devbox.sh`, but unlike the devbox section this flow doesn't mention that the script requires a human-supplied Infisical Universal Auth client ID/secret and persists machine credentials on the host. The assistant section is otherwise framed as unattended/minimal, so a reader running it headless won't know they must provision those credentials first; without them this step prompts and can hang. Worth one sentence noting the provisioning prerequisite (or the platform layer doing it) so the documented flow is reproducible.</violation>
</file>

<file name="scripts/bootstrap/apply-dotfiles.sh">

<violation number="1" location="scripts/bootstrap/apply-dotfiles.sh:230">
P3: The profile resolution collapses distinct failure reasons into one generic message. dotfiles_resolve_profile returns 1 when there is simply no stored/environment profile, but returns 3 when an explicitly configured profile file (DOTFILES_PROFILE_FILE) is set yet missing or unreadable. In all of these cases apply-dotfiles.sh prints 'a supported profile is required' and exits 2, which can mislead a user whose stored profile file actually exists but is unreadable or invalid (return 3). Distinguishing the 'no profile configured yet' case from the 'stored profile is unreadable/invalid' case would make first-run versus broken-state diagnostics clearer.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread scripts/audit/workstation.sh
Comment thread scripts/lib/config-paths.sh Outdated
Comment thread scripts/audit/devbox.sh
Comment thread scripts/lib/config-paths.sh
Comment thread scripts/verify/configure-git.sh
Comment thread scripts/verify/configure-git.sh
Comment thread scripts/verify/profiles.sh
Comment thread scripts/verify/assistant-git-boundary.sh
Comment thread docs/bootstrap.md
Comment thread scripts/bootstrap/apply-dotfiles.sh

@cubic-dev-ai cubic-dev-ai 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.

13 issues found and verified against the latest diff

Confidence score: 2/5

  • scripts/audit/workstation.sh can leak verified trufflehog findings to stdout, which risks exposing secrets in local/CI logs; switch this path to sanitized/machine-readable handling and keep sensitive match content out of normal output.
  • scripts/bootstrap/apply-dotfiles.sh (stage_legacy_config) can leave both staged canonical and legacy configs behind after a failed chezmoi apply, creating persistent config drift on retries; make staging/cleanup idempotent so legacy sources are always tracked and rolled back.
  • The new devbox profile flow (scripts/verify/devbox-services.sh check_launchd_daemons and scripts/audit/devbox.sh) has concrete regression risk from namespace-resolution complexity plus ignoring legacy devbox.env, which can silently fall back to defaults and mis-verify services; resolve env via dotfiles_resolve_config_file and add devbox-focused contract coverage in scripts/verify/audit-contracts.sh.
  • Validation hardening is still needed in scripts/verify/vendor-neutral.sh, scripts/audit/workstation.sh JSON output, and scripts/lib/profile.sh: broad whitelist matching can hide prohibited branding, control characters can break --json, and EOF/newline handling can reject valid profile markers; tighten match scopes and parser escaping/EOF logic to avoid policy bypasses and automation failures.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/verify/devbox-services.sh">

<violation number="1" location="scripts/verify/devbox-services.sh:254">
P2: Custom agent: **Enforce Pragmatic Test Coverage**

The `check_launchd_daemons` function in `scripts/verify/devbox-services.sh` adds new namespace contract resolution and dual-namespace plist globbing that introduces several important success and failure paths, but no tests appear to cover them.

The new logic includes three distinct branches:
1. A successful namespace contract resolution.
2. An exit-code-3 mismatch between `DOTFILES_LAUNCHD_NAMESPACE` and the stored host contract.
3. A generic invalid namespace contract.

Additionally, the loop now globs across both the resolved namespace and the legacy `com.uinaf.*` namespace, which must behave correctly for both legacy-only and mixed-namespace hosts.

Per the **Enforce Pragmatic Test Coverage** rule, important new success and failure paths should be tested. I would suggest adding focused tests (or at minimum fixture exercises and script-level assertions) that exercise:
- a valid namespace with matching plists,
- legacy `com.uinaf` plists only,
- `DOTFILES_LAUNCHD_NAMESPACE` mismatch with the stored contract (expecting exit 3), and
- an invalid or missing namespace contract.</violation>
</file>

<file name="scripts/verify/audit-contracts.sh">

<violation number="1" location="scripts/verify/audit-contracts.sh:33">
P3: SSH2 and PuTTY key detection can regress without failing this contract: both fixtures are owner-only before the only assertion, so ignored files produce the expected zero failures. Add an insecure-mode assertion for each format, then restore owner-only modes for the no-false-positive check.</violation>

<violation number="2" location="scripts/verify/audit-contracts.sh:37">
P2: Custom agent: **Enforce Pragmatic Test Coverage**

This contract test covers workstation and the personal alias, but it leaves the new devbox audit path untested. Since the PR’s main behavior is profile-aware audit handling, adding a devbox assertion here would make the new role-specific contract less likely to regress.</violation>
</file>

<file name="scripts/audit/workstation.sh">

<violation number="1" location="scripts/audit/workstation.sh:40">
P3: `--json` can emit invalid JSON when `DOTFILES_AUDIT_NAME` contains a control character. Escape JSON control characters in `json_string` (or constrain this override) before emitting the audit name.</violation>

<violation number="2" location="scripts/audit/workstation.sh:124">
P1: Custom agent: **Prevent Sensitive Data in Logs**

The default workstation audit can leak scanner findings into normal output. `trufflehog` is still run in prose mode here, so any verified match can be emitted to stdout/stderr, and the script itself notes that prose scanner output may contain secret material. It would be safer to force redacted/JSON-only scanner output or otherwise suppress raw findings before they reach logs.</violation>

<violation number="3" location="scripts/audit/workstation.sh:132">
P2: 1Password references in `.aws`, `.docker`, and shell-session files are never warned about, although those paths are included in the local secret scan. Expand the reference-file set to recurse through these scanned directories so `op://` references do not silently bypass this audit.</violation>
</file>

<file name="scripts/audit/devbox.sh">

<violation number="1" location="scripts/audit/devbox.sh:4">
P2: Pre-migration devbox audits ignore the existing legacy `devbox.env`, so `DEVBOX_USER` and process-compose settings silently fall back to defaults. Resolve this config through `dotfiles_resolve_config_file` like other devbox helpers, preserving documented legacy fallback.</violation>
</file>

<file name="scripts/verify/service-labels.sh">

<violation number="1" location="scripts/verify/service-labels.sh:24">
P3: Custom namespaces are only verified for `healthd`, so a service-specific label regression can ship while this contract passes. Compare the complete custom label set (and the underscore variant) as done for default labels.</violation>
</file>

<file name=".mise/tasks/audit/personal/_default">

<violation number="1" location=".mise/tasks/audit/personal/_default:5">
P3: This task now calls workstation.sh directly, but the sibling `personal/json` task still goes through `scripts/audit/personal.sh`, which sets `DOTFILES_AUDIT_NAME=personal-security`. The two 'personal' alias entries therefore diverge: only the JSON task preserves the `personal-security` audit identity that `scripts/verify/audit-contracts.sh` enforces. Prose output is identical today, so there's no immediate break, but for consistency and to keep the alias's identity intact, call `./scripts/audit/personal.sh` here (no args) just like `json` does.</violation>
</file>

<file name="scripts/lib/profile.sh">

<violation number="1" location="scripts/lib/profile.sh:47">
P2: A valid profile marker without a final newline is rejected as unreadable because `read` returns nonzero at EOF after assigning its final line. Accept a nonempty assigned value before treating this as a read failure.</violation>
</file>

<file name="scripts/verify/vendor-neutral.sh">

<violation number="1" location="scripts/verify/vendor-neutral.sh:29">
P2: Vendor branding can be hidden on any line that also contains an approved external coordinate, so this gate can pass prohibited paths, labels, or prose in the whitelisted files. Match the approved occurrences narrowly (for example, complete expected line forms) rather than treating the whole line as approved.</violation>
</file>

<file name="scripts/bootstrap/apply-dotfiles.sh">

<violation number="1" location="scripts/bootstrap/apply-dotfiles.sh:206">
P1: A failed `chezmoi apply` leaves a newly staged canonical config behind. On retry, `$target` already exists, so `stage_legacy_config` skips it and never adds the legacy source to the cleanup list; both files persist and canonical-precedence consumers can use migrated config despite the failed apply. Defer staging unmanaged files until apply succeeds, or persist/reconcile pending migration state on retry.</violation>
</file>

<file name="docs/bootstrap.md">

<violation number="1" location="docs/bootstrap.md:350">
P3: The new Assistant User section largely duplicates the assistant policy already captured in the new docs/profiles.md ('Identity Policy' and 'Apply a Profile'). Keeping the same GitHub-auth-is-platform-owned, clean-home boundary, and minimal-runtime guidance in both files means future edits to the assistant role will likely be applied to only one of them, letting the other drift. Consider keeping the canonical detail in docs/profiles.md and having the bootstrap Assistant User section link to it for the identity/boundary rules, retaining only the ordered command steps here.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread scripts/audit/workstation.sh
Comment thread scripts/bootstrap/apply-dotfiles.sh Outdated
Comment thread scripts/verify/devbox-services.sh
Comment thread scripts/verify/audit-contracts.sh
Comment thread scripts/audit/devbox.sh Outdated
Comment thread scripts/verify/service-labels.sh Outdated
Comment thread .mise/tasks/audit/personal/_default Outdated
Comment thread scripts/audit/workstation.sh
Comment thread scripts/verify/audit-contracts.sh
Comment thread docs/bootstrap.md

@cubic-dev-ai cubic-dev-ai 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.

12 issues found across 71 files

Confidence score: 3/5

  • In scripts/audit/devbox.sh and scripts/audit/workstation.sh, several audit paths can miss real inputs (legacy config not resolved, symlinked shell/SSH configs skipped, and Git identity read only from ~/.gitconfig), which can create false negatives for secret/config checks — resolve config via dotfiles_resolve_config_file, safely follow symlink targets, and read effective Git config across scopes.
  • scripts/audit/workstation.sh and scripts/lib/profile.sh add substantial branching and exit-path behavior, but current tests mostly check formatting and leave core success/failure flows undercovered, increasing regression risk in security/bootstrap decisions — add scenario-based tests for env fallback, migration paths, probe failures, normalization, and exit codes.
  • In scripts/bootstrap/apply-dotfiles.sh, using cp -pP can preserve permissive legacy modes on migrated credentials, leaving secrets too open and potentially causing later helper rejection — explicitly set staged canonical credential files to 0600 after copy.
  • Policy and status checks can currently misclassify results: scripts/audit/workstation.sh may report INFISICAL token FAIL when probing is unavailable, scripts/verify/vendor-neutral.sh can allow branded text on whitelisted lines, and scripts/lib/profile.sh rejects valid unterminated single-record profiles — tighten these conditions so unavailable probes are distinct, exceptions are exact, and final non-newline records are accepted.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/audit/workstation.sh">

<violation number="1" location="scripts/audit/workstation.sh:49">
P2: A symlinked shell startup or SSH config can contain a secret yet is omitted from this audit, even though the login shell/SSH will consume it. Include symlinked config files (and resolve/scan their targets safely) in both secret and 1Password-reference path enumeration.</violation>

<violation number="2" location="scripts/audit/workstation.sh:100">
P2: Custom agent: **Enforce Pragmatic Test Coverage**

This new audit script contains significant branching logic, but the existing test only verifies JSON output formatting rather than exercising any of the core success/failure paths. At minimum, the subtle login-shell INFISICAL_TOKEN probe and the main conditional sections (Git identity, gh auth, SSH modes, Codex thresholds, Tailscale) should have tests that exercise both passing and failing states so regressions are caught rather than silently accepted.</violation>

<violation number="3" location="scripts/audit/workstation.sh:103">
P2: The 'login shell exports INFISICAL_TOKEN' check reports a FAIL whenever it could not probe the login shell, not just when the token is actually exported. `zsh_login_has_no_infisical_token` starts at 1 and is only set to 0 when `-x "$login_shell"` is true and the shell exits 0; if SHELL is unset, the dscl UserShell lookup returns nothing, or the shell path is stale, the probe is skipped yet the audit prints a hard `fail_check "login shell exports INFISICAL_TOKEN"`. That is a false positive that misattributes an inability to inspect the shell to an actual secret-export leak, which can mislead operators running the workstation security drift audit. Consider treating an unprobeable shell as a warning/unknown rather than a token-export failure.</violation>
</file>

<file name="scripts/verify/vendor-neutral.sh">

<violation number="1" location="scripts/verify/vendor-neutral.sh:29">
P2: Branding added on the same line as an allowed external coordinate is silently accepted, so this gate can miss forbidden installed paths, prose, or config names. Narrow these exceptions to the exact allowed lines/coordinates rather than using substring matches for an entire line.</violation>
</file>

<file name="scripts/audit/devbox.sh">

<violation number="1" location="scripts/audit/devbox.sh:4">
P2: Devbox audits ignore supported legacy configuration when migration has not yet occurred or is retained. Resolve this path with `dotfiles_resolve_config_file` (as `scripts/verify/devbox-services.sh` does) so `DEVBOX_USER` and process-compose settings are audited from the active config.</violation>

<violation number="2" location="scripts/audit/devbox.sh:339">
P2: Custom agent: **Enforce Pragmatic Test Coverage**

The devbox audit script now resolves Git identity by reading only `~/.gitconfig` instead of the effective Git config across all scopes. This is a meaningful change to core audit verification logic, but no tests in the PR cover the new resolution behavior (e.g., asserting correct detection when identity lives in that file, or proper failure when it does not). Rule 4 flags important logic changes that lack test coverage for the main success and failure paths.</violation>
</file>

<file name="scripts/lib/profile.sh">

<violation number="1" location="scripts/lib/profile.sh:1">
P2: Custom agent: **Enforce Pragmatic Test Coverage**

This file adds core profile-resolution logic with multiple success and failure paths (env-var fallback, legacy-file migration, trim/normalize, and distinct exit codes for invalid/missing profiles), but no tests were added alongside it. Under pragmatic test coverage, core business logic like this should have tests for its main success and failure paths. Consider adding tests for `dotfiles_resolve_profile`, `dotfiles_normalize_profile`, and the role predicates to guard against regressions in profile selection.</violation>

<violation number="2" location="scripts/lib/profile.sh:47">
P2: A valid one-record profile without a trailing newline is rejected as unreadable, so manually persisted profile markers can block bootstrap instead of resolving their role. Accept the final unterminated nonempty record after the one-record check.</violation>
</file>

<file name=".mise/tasks/audit/personal/_default">

<violation number="1" location=".mise/tasks/audit/personal/_default:5">
P2: The personal profile's default command now calls scripts/audit/workstation.sh directly, so it no longer carries the personal-security identity that the personal.sh compatibility wrapper (DOTFILES_AUDIT_NAME=personal-security) provides. The sibling personal/json subcommand still routes through personal.sh and reports personal-security, and scripts/verify/audit-contracts.sh explicitly guards that this label be preserved, so the two subcommands of the same profile now disagree on audit identity. Suggest routing _default through the same wrapper as json for consistency.</violation>
</file>

<file name="scripts/bootstrap/apply-dotfiles.sh">

<violation number="1" location="scripts/bootstrap/apply-dotfiles.sh:206">
P2: Migrated credentials can retain a permissive legacy mode because `cp -pP` preserves permissions. Set staged canonical files to `0600`, matching the documented secret-file contract and avoiding later helper rejection.</violation>
</file>

<file name="scripts/bootstrap/install-devbox-service-daemons.sh">

<violation number="1" location="scripts/bootstrap/install-devbox-service-daemons.sh:91">
P3: `--print-labels` resolves the namespace from the default or env var only, ignoring a persisted `~/.config/dotfiles/launchd-namespace` contract file, whereas the actual install path resolves through `dotfiles_resolve_launchd_namespace_contract` (file-backed). On a host that already persisted a custom namespace, `--print-labels` would print `local.dotfiles.*` labels while the installer writes labels under the stored custom namespace, so the printed labels do not match what is really installed. Using the same contract resolution in the print path would keep the two consistent.</violation>

<violation number="2" location="scripts/bootstrap/install-devbox-service-daemons.sh:92">
P3: When the stored `~/.config/dotfiles/launchd-namespace` contract fails validation, the script collapses every non-code-3 return path into the message 'LaunchDaemon namespace must contain dot-separated letters, numbers, hyphens, or underscores'. But `dotfiles_resolve_launchd_namespace_contract` also returns 4 for a namespace file that is not mode 0600 or is not owned by the target user, which is a permissions/ownership problem, not a naming problem. An operator hitting the 4 path would be told to fix the name when the real issue is the file mode or owner. Consider distinguishing return code 4 and printing a targeted message about the stored file's mode/ownership instead.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread scripts/audit/workstation.sh
Comment thread scripts/verify/vendor-neutral.sh
Comment thread scripts/audit/devbox.sh Outdated
Comment thread scripts/lib/profile.sh Outdated
Comment thread .mise/tasks/audit/personal/_default Outdated
Comment thread scripts/audit/devbox.sh
Comment thread scripts/audit/workstation.sh
Comment thread scripts/lib/profile.sh
Comment thread scripts/bootstrap/install-devbox-service-daemons.sh
Comment thread scripts/bootstrap/install-devbox-service-daemons.sh Outdated

@cubic-dev-ai cubic-dev-ai 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.

14 issues found across 71 files

Confidence score: 2/5

  • scripts/verify/profiles.sh still allows GIT_CONFIG to slip through assistant boundary checks, which can let injected global Git settings pass as a clean profile and weaken trust in the verification gate—add a rejection fixture for GIT_CONFIG and clear that env var during verification.
  • scripts/audit/devbox.sh and scripts/lib/audit.sh can silently skip or abort key audits (legacy config paths forced to one location, and rejected audit.env symlink exiting under set -e), so important failures may never be reported to users—restore legacy path loading and convert resolver failures into explicit fail_check results.
  • scripts/verify/devbox-services.sh and scripts/verify/service-labels.sh currently mismatch service ownership/label scope (all-user globs and a single-label check), which can produce both false failures and missed daemon misconfiguration—scope checks to devbox_user and validate the full expected label set.
  • scripts/bootstrap/apply-dotfiles.sh has migration safety gaps: a symlinked ~/.config/dotfiles can redirect writes, and failed applies can strand duplicated non-managed files that block retries—reject symlinked canonical dirs up front and make staging transactional (or defer non-managed copies until success).
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="docs/profiles.md">

<violation number="1" location="docs/profiles.md:50">
P2: The Identity Policy paragraph conflates profiles: configure-git.sh does not disable commit/tag signing for workstation or devbox (devbox defaults signing on, per README/AGENTS), and the workload-owned marker is written only for assistant. An operator reading this could wrongly expect devbox/workstation signing to be off. Recommend clarifying that signing is disabled and the workload marker set only for the assistant workload identity.</violation>
</file>

<file name=".mise/tasks/audit/personal/_default">

<violation number="1" location=".mise/tasks/audit/personal/_default:5">
P2: Running the personal audit via _default now reports `audit:"workstation-security"` instead of `personal-security` because this bypasses the personal.sh wrapper that set `DOTFILES_AUDIT_NAME=personal-security`. This diverges from the sibling `personal/json` task and from the audit-contracts verification which still expects `personal-security`. Keep calling `./scripts/audit/personal.sh` so the personal audit keeps its distinct identity.</violation>
</file>

<file name="scripts/audit/devbox.sh">

<violation number="1" location="scripts/audit/devbox.sh:4">
P2: Pre-migration devbox audits no longer load either legacy config file: both paths are forced to `~/.config/dotfiles`, so `DEVBOX_USER`, process-compose settings, and machine-credential mode checks are silently skipped. Resolve these paths through `dotfiles_resolve_config_file` after loading the helper so audits remain usable until `apply-dotfiles.sh` migrates state.</violation>
</file>

<file name="scripts/verify/vendor-neutral.sh">

<violation number="1" location="scripts/verify/vendor-neutral.sh:10">
P2: Owner-branded file paths are not detected even though paths are part of the vendor-neutral boundary. Add a filename/path scan alongside the content scan, including tracked and non-ignored untracked paths.</violation>
</file>

<file name="scripts/verify/devbox-services.sh">

<violation number="1" location="scripts/verify/devbox-services.sh:255">
P2: A devbox verification can fail because another Unix user's daemon is unloaded or has different ownership: these globs select every user's service label. Scope the plist names to `devbox_user`, which the local contract identifies as the target identity.</violation>
</file>

<file name="scripts/verify/audit-contracts.sh">

<violation number="1" location="scripts/verify/audit-contracts.sh:37">
P2: The repo gate fails when callers export `DOTFILES_AUDIT_NAME`, because this contract test inherits that override while asserting the default workstation name. Pin the expected audit name for this isolated invocation.</violation>
</file>

<file name="scripts/lib/audit.sh">

<violation number="1" location="scripts/lib/audit.sh:278">
P2: A rejected legacy `audit.env` symlink aborts the audit silently under `set -e`, so callers receive no JSON/prose result or recorded audit failure. Handle resolver failure here and convert it to `fail_check` before returning.</violation>
</file>

<file name="Brewfile.assistant">

<violation number="1" location="Brewfile.assistant:2">
P3: The top comment names OpenClaw and Hermes in Brewfile commentary, which conflicts with the AGENTS.md guidance to keep commentary vendor-neutral and not reference external agent frameworks from the Brewfile. Consider rewording to describe the boundary without naming specific frameworks so the role layer stays portable and doesn't drift into framework-specific claims.</violation>
</file>

<file name="Brewfile.developer">

<violation number="1" location="Brewfile.developer:15">
P2: README documents gh-stack as installed via this developer layer, but the new Brewfile.developer never installs it. Developer profiles (workstation/devbox) will silently not get gh-stack, contradicting the documented behavior. Add the gh-stack entry or update README to drop that claim so the docs and the shared layer stay in sync.</violation>
</file>

<file name="scripts/lib/config-paths.sh">

<violation number="1" location="scripts/lib/config-paths.sh:16">
P2: A legacy directory, FIFO, or other special file is treated as a config file. This can make helpers fail or block while sourcing it; accept only regular legacy files and fail closed for other existing path types.</violation>
</file>

<file name="scripts/verify/profiles.sh">

<violation number="1" location="scripts/verify/profiles.sh:414">
P1: Assistant boundary verification still accepts the `GIT_CONFIG` override, so a shell can inject global Git settings while the audit reports a clean assistant profile. Add a rejection fixture for `GIT_CONFIG` and clear it in clean-boundary invocations, alongside `GIT_CONFIG_GLOBAL`.</violation>
</file>

<file name="scripts/bootstrap/apply-dotfiles.sh">

<violation number="1" location="scripts/bootstrap/apply-dotfiles.sh:160">
P2: Migration can write local config through a symlinked canonical directory because only `~/.config/uinaf` is checked. Reject a symlinked `~/.config/dotfiles` before staging so credentials and config do not move to an unintended target.</violation>

<violation number="2" location="scripts/bootstrap/apply-dotfiles.sh:206">
P2: A failed apply leaves copied non-managed legacy files stranded in both namespaces, and retry treats the staged canonical copy as a conflict. Make staging transactional or defer non-managed copies until after a successful apply so retry completes the move.</violation>
</file>

<file name="scripts/verify/service-labels.sh">

<violation number="1" location="scripts/verify/service-labels.sh:23">
P2: Custom agent: **Enforce Pragmatic Test Coverage**

The custom LaunchDaemon namespace verification only checks a single label (`healthd`) instead of comparing the full expected label set. If the installer applies the custom namespace to only one service while leaving others on the default `local.dotfiles.*` prefix, the `grep -Fqx` check would still pass and the bug would be undetected. Please compare the complete output (all 4 service labels) for custom namespace checks, following the same pattern already used for the default namespace comparison above.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread scripts/verify/profiles.sh Outdated
Comment thread docs/profiles.md Outdated
Comment thread .mise/tasks/audit/personal/_default Outdated
Comment thread scripts/audit/devbox.sh Outdated
Comment thread scripts/verify/vendor-neutral.sh
Comment thread scripts/lib/config-paths.sh Outdated
Comment thread scripts/bootstrap/apply-dotfiles.sh Outdated
Comment thread scripts/bootstrap/apply-dotfiles.sh Outdated
Comment thread scripts/verify/service-labels.sh
Comment thread Brewfile.assistant Outdated

@cubic-dev-ai cubic-dev-ai 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.

13 issues found and verified against the latest diff

Confidence score: 2/5

  • scripts/bootstrap/install-git-hooks.sh changes ownership detection/marker generation in a core installer path, so existing managed hooks may be misclassified and then overwritten or skipped unexpectedly; align the regex/marker handling with legacy forms and add migration-safe matching tests.
  • scripts/audit/devbox.sh and scripts/bootstrap/apply-dotfiles.sh introduce migration-edge regressions where legacy ~/.config/uinaf state is ignored and retry flows can leave duplicated unmanaged files, leading to incorrect supervisor auditing and persistent config drift; preserve legacy-path resolution and make retries idempotent.
  • scripts/lib/audit.sh skips SSH private keys reached via symlink, which can let permissive linked IdentityFile targets pass audit and weaken key-permission enforcement; follow symlinks when selecting regular files so linked keys are checked.
  • scripts/verify/vendor-neutral.sh plus scripts/bootstrap/install-devbox-service-daemons.sh/scripts/verify/devbox-services.sh widen bypass/collision behavior (branding checks can be evaded, canonical daemon labels can be replaced, and legacy symlinks can cause silent verify exits), increasing policy and service-state regression risk; narrow exceptions to explicit legacy patterns, detect label collisions before overwrite, and fail with explicit diagnostics on resolver errors.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/bootstrap/install-git-hooks.sh">

<violation number="1" location="scripts/bootstrap/install-git-hooks.sh:23">
P2: Custom agent: **Enforce Pragmatic Test Coverage**

The installer script `scripts/bootstrap/install-git-hooks.sh` changed its core ownership-detection regex (`grep -Eq '^# (uinaf-)?dotfiles: pre-push$'`) and the generated marker comment, but there are no tests covering the three critical runtime paths: (1) an existing hook with the legacy `uinaf-dotfiles: pre-push` marker is recognized and rewritten without backup, (2) an existing hook with the new `dotfiles: pre-push` marker is recognized on re-run without backup, and (3) a foreign hook is backed up before being overwritten. The repo's verification suite (`scripts/verify/*.sh`, `scripts/audit/*.sh`) only performs syntax and lint checks on this file, not behavioral verification. Consider adding a focused test fixture or BATS test that exercises these idempotency and backup paths.</violation>
</file>

<file name="scripts/verify/devbox-services.sh">

<violation number="1" location="scripts/verify/devbox-services.sh:9">
P2: A legacy config symlink now makes verification exit silently during initialization, including when machine auth is optional or the sudo credential check is not selected. Handle resolver failures explicitly and report the rejected path so the normal required/optional config behavior remains observable.</violation>
</file>

<file name="scripts/verify/audit-contracts.sh">

<violation number="1" location="scripts/verify/audit-contracts.sh:33">
P3: SSH2 and PuTTY private-key detection can regress without failing this contract, since compliant modes produce the same zero count whether they are recognized or skipped. Make at least those fixtures group/world-readable and assert their expected failures, while retaining the `authorized_keys` control.</violation>
</file>

<file name="scripts/verify/service-labels.sh">

<violation number="1" location="scripts/verify/service-labels.sh:20">
P3: Repository verification becomes host-state-dependent when macOS has an `example` account: its stored namespace overrides the default being asserted, so this valid contract causes the gate to fail. Use an isolated nonexistent/generated probe user (and derive expected labels from it), or test label generation through the sourced helper without a real-user contract.</violation>
</file>

<file name="scripts/audit/devbox.sh">

<violation number="1" location="scripts/audit/devbox.sh:4">
P2: Devboxes not yet migrated from `~/.config/uinaf` will ignore their existing `devbox.env` and machine config, auditing defaults instead of the configured supervisor and skipping the existing machine-file permission check. Resolve both paths through `dotfiles_resolve_config_file`, as `verify/devbox-services.sh` does, so pre-migration audits remain meaningful.</violation>
</file>

<file name="scripts/lib/audit.sh">

<violation number="1" location="scripts/lib/audit.sh:123">
P2: SSH private keys reached through symlinks are skipped, allowing a group/world-readable linked key to pass this permission audit. Follow symlinks while selecting regular files so linked `IdentityFile` targets receive the same mode check.</violation>
</file>

<file name="scripts/verify/vendor-neutral.sh">

<violation number="1" location="scripts/verify/vendor-neutral.sh:10">
P2: Owner branding added anywhere else in `vendor-neutral.sh` will never be checked, because this pathspec omits the entire file. Keep the scan self-checking and narrowly exempt only the scanner's necessary legacy-pattern literals.</violation>

<violation number="2" location="scripts/verify/vendor-neutral.sh:49">
P2: An active owner-branded config path can bypass this verification in all listed files. Match legacy migration wording or the specific legacy-path variables/tests instead of accepting every `config/uinaf` occurrence.</violation>

<violation number="3" location="scripts/verify/vendor-neutral.sh:54">
P2: New owner-branded content in these scripts can bypass the vendor-neutral gate merely by including `dotfiles`. Restrict this exception to the known legacy `uinaf-dotfiles` SSH and pre-push marker forms.</violation>
</file>

<file name="scripts/bootstrap/apply-dotfiles.sh">

<violation number="1" location="scripts/bootstrap/apply-dotfiles.sh:174">
P2: A failed apply followed by a retry leaves migrated unmanaged files permanently duplicated: the first run creates `~/.config/dotfiles/devbox.env`, then this branch treats it as a pre-existing conflict and retains `~/.config/uinaf/devbox.env`. Track partial migrations durably or defer copying unmanaged files until after a successful apply, so retries can complete the move without treating their own staged target as a conflict.</violation>
</file>

<file name="scripts/lib/profile.sh">

<violation number="1" location="scripts/lib/profile.sh:52">
P2: Profiles saved as a single line without a trailing newline can fail resolution even though the file already passed the one-line check. The `read` status check treats EOF-without-newline as an error; allowing non-empty `value` here keeps valid persisted profiles working.</violation>
</file>

<file name="scripts/bootstrap/install-devbox-service-daemons.sh">

<violation number="1" location="scripts/bootstrap/install-devbox-service-daemons.sh:93">
P3: `--print-labels` fails for macOS accounts whose home path contains spaces because `awk` discards everything after the first path token. Parse the full value following `NFSHomeDirectory:` instead.</violation>

<violation number="2" location="scripts/bootstrap/install-devbox-service-daemons.sh:102">
P2: A pre-existing daemon using the new generic label is unloaded and its plist overwritten on first install. Detect a canonical-label collision (or require an explicit replacement mode) before persisting the namespace and calling `install_job`.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread scripts/bootstrap/install-git-hooks.sh
Comment thread scripts/verify/devbox-services.sh
Comment thread scripts/audit/devbox.sh Outdated
Comment thread scripts/lib/audit.sh
Comment thread scripts/verify/vendor-neutral.sh Outdated
Comment thread scripts/lib/profile.sh Outdated
Comment thread scripts/bootstrap/install-devbox-service-daemons.sh
Comment thread scripts/verify/audit-contracts.sh
Comment thread scripts/verify/service-labels.sh
Comment thread scripts/bootstrap/install-devbox-service-daemons.sh
BREAKING CHANGE: Existing users must manually move former owner-specific config, reconfigure Git, and retire conflicting services before applying a workstation, devbox, or assistant profile. See docs/migrating-to-role-profiles.md.
@altaywtf altaywtf changed the title feat: add per-user role profiles feat!: add per-user role profiles Aug 1, 2026
@altaywtf
altaywtf merged commit 05d269d into main Aug 1, 2026
4 of 5 checks passed
@altaywtf
altaywtf deleted the feat/user-role-profiles branch August 1, 2026 11:58
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