Skip to content

fix(admission): a busy process outside any repository is host load, not uncovered work of unknown ownership #294

Closed

Description

@entelecheia

Problem

dot admit deferred agent-hub's local checks five times on 2026-10-20 (exit 75). Each time the blocker was a background process that runs no build or test, in no repository:

  • Maru's dotfiles commands (parent Maru.app), reported as dotfiles(pid=...,parent=8703,repo=);
  • claude-mem's observer, a one-turn headless claude --max-turns=1 summarizer whose cwd is ~/.claude-mem/observer-sessions.

Cause, in internal/admission/uncovered.go:

  • unownedHeavyJobs counts any row at 100% CPU or more as heavy, whatever its name (heavyweight(...) || r.cpu >= 100).
  • resolveJobDirectories resolves the cwd, and repositoryIdentity fails when the walk reaches / without a .git. That leaves Repository empty.
  • filterUncovered treats an empty Repository as unknown ownership and blocks every caller.

So a busy process whose cwd is known and lies outside every repository (an app launched with cwd /, or a tool's state directory) blocks all admission in every repository. Its CPU use is host load, which the pressure thresholds already cover.

Decisions

  • D1: repositoryIdentity returns a sentinel (errNotRepository) when the walk ends without finding .git. Every other failure (permission, invalid Git pointer, symlink error) stays a plain error, meaning unknown.
  • D2: HeavyJob records two facts:
    • CPUOnly: the row matched only the CPU rule, not heavyweight(name, args);
    • OutsideRepository: its cwd was observed and repositoryIdentity returned errNotRepository.
  • D3: filterUncovered no longer counts a job that is both CPUOnly and OutsideRepository as unknown ownership. The maintenance rule is unchanged: a maintenance-shaped job still blocks the maintenance class.
  • D4: unchanged:
    • build- or test-shaped work outside a repository still blocks;
    • a job whose cwd could not be observed (lsof failure, partial batch) still blocks;
    • the 20-job inventory cap and the pressure thresholds are unchanged.

Acceptance criteria

  • AC1: filterUncovered drops a CPUOnly && OutsideRepository job, and still blocks:
    • a CPU-only job of unknown ownership (cwd unobserved, or a non-sentinel resolution error);
    • a heavyweight-shaped job outside a repository;
    • a maintenance-shaped CPU-only job outside a repository for the maintenance class (table test).
  • AC2: repositoryIdentity returns errNotRepository for a temporary directory with no .git above it, and a plain error for an invalid Git pointer (test).
  • AC3: unownedHeavyJobs sets CPUOnly only when the CPU rule alone matched (test with claude at 150% CPU and cargo build at 150% CPU).
  • AC4: make test (GOMAXPROCS=2 -p 2 -parallel 2) and make lint pass; CI green.

Activity

  1. entelecheia commented on Oct 19, 2026

    @entelecheia
    MemberAuthor

    Amendment (PR #295 review, 2026-10-20)

    • D5: the same-repository clause matches only a known repository: j.Repository != "" && j.Repository == project. Before this, when the caller ran outside every repository (project == ""), every job with an empty Repository, including an exempted CPU-only job outside a repository, matched "same repository" and blocked. Unknown jobs still block through the unknown-ownership rule.
    • D6: the exemption trusts a process's cwd as the repository it works on. Unshaped work whose cwd lies outside every repository but which builds or tests a repository (for example a Gradle daemon under ~/.gradle, a VM process running a container build, or a script runner launched from ~) is no longer deferred, and a single such process can stay under the pressure thresholds on a many-core host. Native compiler children (rustc, clang, swift-frontend, ld) are still name-shaped and still block. This is recorded as an accepted ceiling in docs/CEILINGS.md, with a code marker.
    • AC5: tests pin:
      • resolveJobDirectories sets OutsideRepository=true for a live child process whose cwd is a temporary directory outside every repository, and false for one whose cwd was removed after start;
      • repositoryIdentity on a nonexistent path is not errNotRepository;
      • filterUncovered drops the exempted job when project == "".
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions