Documentation
¶
Overview ¶
Package discovery scans a repository directory and reports what an AI coding agent needs to select a project and begin useful work, per ADR-0032 ("Agent-First Development Experience"), P0: "Add Modulex agent project discovery and command classification" (Jira MOD-64), step 1 of the ADR's "Standard agent workflow": `modulex agent discover` identifies the repository root, projects, modules, composition roots, instruction files, Make targets, CI workflows, and available indexes.
This package is a standalone leaf package, like provenance (github.com/mediusfy/modulex/provenance): it does not import the core modulex package, and depends on provenance only for the CommandClass enum so command classification stays consistent with the provenance/handoff schema rather than inventing a parallel one. Discover is otherwise pure standard library and never consults global, user-scoped configuration (no ~/.claude, no ~/.kimi-code, nothing outside the given root and PATH), per the ADR's "discovery works without global hooks" acceptance criterion.
Discover is read-only: it enumerates files, parses text, and checks PATH, but it never executes a discovered binary or a Make target. The one external process it runs is `git status --porcelain`, used only to detect a dirty worktree.
See docs/planning/agent-discovery-guide.md for the full guide, including the nested-module-boundary behavior and the command-classification rule table's fail-safe default.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ClassificationRules = []ClassificationRule{ { Pattern: regexp.MustCompile(`^git (status|diff|log)\b`), Class: provenance.ClassSafe, Reason: "read-only git inspection command; always allowed per agent-safety-policy.md", }, { Pattern: regexp.MustCompile(`^git (reset --hard|clean -f|branch -D)\b`), Class: provenance.ClassDestructive, Reason: "discards uncommitted work or deletes a branch irreversibly; requires explicit human approval before running per agent-safety-policy.md", }, { Pattern: regexp.MustCompile(`^git (push|tag)\b`), Class: provenance.ClassApprovalRequired, Reason: "pushes to a remote or creates a tag; external mutation requires explicit, current-session human approval per agent-safety-policy.md", }, { Pattern: regexp.MustCompile(`^git (add|commit)\b`), Class: provenance.ClassMutating, Reason: "writes to the local git index/history on the current branch; allowed on a feature branch, never directly on main", }, { Pattern: regexp.MustCompile(`^go (build|vet|test)\b`), Class: provenance.ClassSafe, Reason: "compiles or statically checks code with no side effects; always allowed per agent-safety-policy.md", }, { Pattern: regexp.MustCompile(`^go mod download\b`), Class: provenance.ClassNetworked, Reason: "fetches module content from the configured module proxy", }, { Pattern: regexp.MustCompile(`^make fmt\b`), Class: provenance.ClassMutating, Reason: "rewrites source files in place (gofmt -s -w .)", }, { Pattern: regexp.MustCompile(`^make deps\b`), Class: provenance.ClassNetworked, Reason: "downloads Go module dependencies (go mod download)", }, { Pattern: regexp.MustCompile(`^make vuln\b`), Class: provenance.ClassNetworked, Reason: "fetches the govulncheck vulnerability database over the network", }, { Pattern: regexp.MustCompile(`^make release\b`), Class: provenance.ClassApprovalRequired, Reason: "tags and pushes a release (git tag + git push origin); tagging/publishing a release always requires explicit human approval per agent-safety-policy.md, regardless of any standing autonomy instruction", }, { Pattern: regexp.MustCompile(`^make publish-godev\b`), Class: provenance.ClassApprovalRequired, Reason: "networked AND externally visible (asks proxy.golang.org/pkg.go.dev to index a release); approval_required takes precedence over networked per this rule table's documented tie-break", }, { Pattern: regexp.MustCompile(`^make (build|test|test-arch|lint|help|check-[a-z-]+)\b`), Class: provenance.ClassSafe, Reason: "runs a read-only build, test, or verification target (wraps go build/test/vet, golangci-lint run, or a read-only check script) with no network or mutating side effects", }, }
ClassificationRules is the ordered, first-match-wins rule table ClassifyCommand consults, built from docs/planning/agent-safety-policy.md's command-classification table for ADR-0032 P0 (Jira MOD-64). It reuses provenance.CommandClass rather than a parallel enum, per that package's whole reason for existing as a separate, composable leaf package.
Rules are grouped by command family and ordered from narrower/more consequential to broader/safer within each family; add new rules where they logically belong rather than only appending, so an unusual future pattern overlap stays easy to spot by reading the table top to bottom.
Functions ¶
func ClassifyCommand ¶
func ClassifyCommand(cmd string) (provenance.CommandClass, string)
ClassifyCommand classifies cmd (a full command line, e.g. "make release VERSION=v0.2.0" or "git push origin main") by matching it against ClassificationRules in order and returning the first match's class and reason.
Fail-safe default: if no rule matches, ClassifyCommand returns provenance.ClassApprovalRequired rather than defaulting to safe. An unrecognized command must never silently pass through as though it had been vetted; per agent-safety-policy.md, "when in doubt about whether an action is safe, an agent must treat it as requiring approval rather than assuming permission."
Types ¶
type ClassificationRule ¶
type ClassificationRule struct {
Pattern *regexp.Regexp
Class provenance.CommandClass
Reason string
}
ClassificationRule maps a command pattern to a provenance.CommandClass and a human-readable reason. Pattern is matched against the trimmed command string (e.g. "make release VERSION=v0.2.0", "git push origin main"); Reason should be specific enough that a human reviewing an agent transcript understands why the command was classified the way it was without re-deriving it themselves.
type CompositionRoot ¶
CompositionRoot describes one directory identified as a candidate composition root (a place where modules are wired together into a runnable program), along with why it was flagged.
type GoModule ¶
GoModule describes one discovered Go module: its path relative to the discovery root ("." for the root module itself) and the module path declared by its go.mod's "module" directive.
type IndexStatus ¶
IndexStatus reports whether a well-known semantic-index directory (e.g. .codegraph, .tokensave) is present at the discovery root. Absence is reported explicitly rather than omitted, per ADR-0032's "missing tools and optional services are reported explicitly" acceptance criterion.
type InstructionFile ¶
InstructionFile describes one discovered agent-instruction file (e.g. AGENTS.md, CLAUDE.md) and where it was found.
type Repository ¶
type Repository struct {
// Root is the absolute path Discover resolved the given root to.
Root string `json:"root"`
// IsGitRepo is true if Root contains a .git entry (directory or file,
// the latter covering git worktrees). It does not imply Dirty is
// meaningful in the same run if the entry turns out not to be a usable
// git repository (see Discover's doc comment).
IsGitRepo bool `json:"is_git_repo"`
// Dirty is true if `git status --porcelain` reported any uncommitted
// changes. Always false when IsGitRepo is false; this is the
// "not a git repository" case, not an error.
Dirty bool `json:"dirty"`
// Modules lists every Go module found under Root, including Root's own
// module if it has a go.mod. Walking stops descending into a directory
// once a go.mod is found there: a nested module's own subdirectories
// belong to it, not to the parent scan, mirroring how `go list ./...`
// treats module boundaries.
Modules []GoModule `json:"modules"`
// CompositionRoots lists directories identified as candidate
// composition roots: every direct child directory of examples/ (if
// present), plus any directory anywhere under Root containing a Go
// file with a package-level func main().
CompositionRoots []CompositionRoot `json:"composition_roots"`
// InstructionFiles lists every well-known agent-instruction file found
// anywhere under Root (AGENTS.md, CLAUDE.md, .cursorrules,
// copilot-instructions.md).
InstructionFiles []InstructionFile `json:"instruction_files"`
// MakeTargets lists target names parsed from a Makefile at Root, if
// present, both from .PHONY: lines and plain "target:" lines at column
// zero. Empty (never nil) if there is no Makefile at Root.
MakeTargets []string `json:"make_targets"`
// CIWorkflows lists *.yml/*.yaml file names under .github/workflows/ at
// Root, if present.
CIWorkflows []string `json:"ci_workflows"`
// Indexes reports presence/absence of well-known semantic-index
// directories at Root (.codegraph, .git, .tokensave).
Indexes []IndexStatus `json:"indexes"`
// Tools reports presence/absence on PATH of commonly-needed binaries
// (go, git, golangci-lint, gofmt, docker). Discover never executes any
// of them.
Tools []ToolStatus `json:"tools"`
}
Repository is the result of Discover: everything found by walking a repository root and checking PATH, with no dependency on global or user-scoped state. Every slice field is deterministically sorted and never nil, so two Discover calls against the same on-disk repository state produce byte-identical JSON via json.Marshal (the same discipline as provenance.Envelope and the core module's Manager.Diagnostics / Manager.ModuleContract).
func Discover ¶
func Discover(root string) (Repository, error)
Discover scans root (which must be an existing directory; never the process's implicit working directory — the caller always supplies it explicitly, even if that means passing "." for their own current directory) and reports Go modules, composition roots, instruction files, Make targets, CI workflows, semantic indexes, available tools, and git dirty-worktree state.
Discover never mutates the repository and never executes a discovered binary or Make target; it only reads files, walks directories, checks PATH, and (for dirty-worktree detection only) runs `git status --porcelain`.
type ToolStatus ¶
type ToolStatus struct {
Name string `json:"name"`
Present bool `json:"present"`
// Path is the resolved absolute path from exec.LookPath, empty when
// Present is false.
Path string `json:"path,omitempty"`
}
ToolStatus reports whether a commonly-needed binary is available on PATH. A missing tool is reported explicitly (Present: false) rather than silently dropped from the result.