Skip to content

dircue 1.0.0

Choose a tag to compare

@github-actions github-actions released this 28 Sep 17:47
· 12 commits to main since this release
99507c2

dircue 1.0.0

Try it

dircue map --summary /path/to/checkout
dircue map --json /path/to/checkout > map.json

The directory-map guide documents the schema, coverage semantics, attachments, comparison, routing and SARIF location support. The compatibility policy lists what 1.x keeps stable.

dircue 1.0 adds the map. One deterministic, offline command answers what an unfamiliar directory contains and how it is built and run: projects, deployables, declared interfaces and capabilities, and the relationships between them. Every fact carries evidence identifying its source file and rule; a source span is included when the analyzer can locate one. Every question has a coverage status. The strict Linguist-compatible language commands remain available with their 0.9 output contract; changes to other commands are listed below.

The map

  • dircue map [path] writes a portable map document (schema/map.schema.json, schema version 1.0.0) for a committed Git tree or an ordinary directory. Redirected output defaults to JSON; a terminal gets a one-screen summary (--summary). Inspected content is never executed.

  • Output. Nodes (content populations, components, deployables, interfaces, capabilities, packages) and edges (contains, member_of, depends_on_local, depends_on, builds, runs, exposes, declares, uses_capability, packaged_in, analyzed_by) have stable IDs derived from paths and declared names. Evidence identifies the source file and rule, with a span when available. Coverage (complete, partial, unknown, not_run, tool_error) includes named reasons. Dockerfile COPY observations include the copy_source and copy_source_stage fact kinds; recognized Maven archive attribution uses the dockerfile_copy_source_matches_maven_archive edge reason.

  • What complete means. A question, node or edge says complete only when its evidence is exhaustive for its scope. Heuristic attribution, such as a capability credited to a component by directory containment or a Dockerfile shared by several projects, is partial, with a named reason. Content problems never abort a map; they degrade coverage.

  • Components come from static, bounded parsing of 36 component kinds, reported under 27 ecosystem values:

    • npm, Go, Cargo and Python (pyproject, uv, setup.cfg with project metadata, Pipfile, requirements files and their -r includes);
    • Maven, Gradle, .NET and Kbuild;
    • Ruby (Bundler, gemspec, Rails application name) and PHP (Composer);
    • Swift, Dart (including path: dependencies), Elixir, Erlang, Scala (sbt) and Haskell;
    • CMake, Meson and Autoconf;
    • Deno, Bazel, Zig, Julia, R, Clojure and Perl.

    Each component has a role: primary, test, fixture, example, vendored, docs or tooling. The summary leads with primary components. Local project relationships (depends_on_local) come from path, workspace and module references, including Maven reactor sibling dependencies and pub path: dependencies.

  • Deployables:

    • Dockerfiles (named by directory);
    • Compose services;
    • Kubernetes objects, one per kind, name and namespace, with every declaring manifest as evidence;
    • Helm charts, one per chart, with static values.yaml images;
    • Terraform modules, one per directory;
    • SAM and Serverless functions, .NET Aspire app hosts, and CI workflows;
    • Maven WAR and EAR packaging, as archive deployables named by finalName or Maven's artifactId-version default, with same-file properties resolved.
  • How things build and run. builds and runs edges come from static declarations:

    • Dockerfile co-location;
    • Dockerfile copies that identify a Maven WAR/EAR artifact (dockerfile_copy_source_matches_maven_archive);
    • Skaffold artifacts and exact image references;
    • SAM CodeUri;
    • Aspire AddProject<>();
    • Maven WAR/EAR packaging;
    • GitHub Actions working directories.

    A CodeUri or working directory belongs to the nearest enclosing project root. When that root holds more than one project, the path leaves the repository, or a working directory is created at run time rather than committed, no edge is emitted and the reference is partial with a named reason.

  • Interfaces:

    • Go package main binaries;
    • Rust, npm and Python entry points;
    • Spring Boot applications and Maven/Gradle main classes;
    • module-level Flask application objects;
    • declared ports (EXPOSE, including ARG/ENV defaults; Compose ports/expose; Kubernetes containerPort; application listener keys);
    • gRPC services and operations;
    • OpenAPI and Swagger documents and their operations;
    • AsyncAPI documents and GraphQL schemas.

    Runtime prerequisites (npm engines, requires-python) and build backends are recorded as component properties (runtime_requirements, build_backend, build_script), not as interfaces.

  • Capabilities (datastores, caches, messaging, search, object storage, auth, HTTP clients, cloud SDKs and more) come from an ecosystem-aware catalog of the most common packages per ecosystem, from declared configuration keys, connection strings and Prisma datasource providers, and from top-level Go and Python imports (including sqlite3). Because Go's net/http also serves inbound requests, it counts as an HTTP client only where a file references a client API; net/http/pprof, httptest, fcgi and cgi never do (rule). There is one capability node per owning component. Optional dependencies (extras, optional and peer dependencies) are conditional. Development-only ones (npm devDependencies, PEP 735 dependency groups, pub dev_dependencies, Pipfile [dev-packages], Gemfile development and test groups) are excluded. Maven test scope is not yet distinguished (#150).

Source identity, forests and hostile filesystems

  • Directory maps carry a Git-compatible tree ID computed without Git. A clean checkout has the same ID as its commit's tree, so map compare recognizes the same source across Git and directory maps. Control it with --set source.digest=git|raw|off. On Windows, where Git checks out without executable bits or symlinks and matches names case-insensitively, the digest is partial (windows_checkout_semantics).
  • map --forest discovers nested, bare and submodule Git roots, summarizes dependency and build-output trees (node_modules, virtualenvs, target/, …), and accounts for every remaining file. Remote credentials are redacted.
  • FIFOs, sockets, devices, symlink loops, escaping symlinks and unreadable directories are skipped with warnings and partial coverage, never followed or fatal.

Joining deeper tools, without importing findings

  • map --attach KIND=PATH joins saved Syft, SARIF 2.1.0, OWASP Noir and Bifrost reports:
    • Only facts and run coverage are imported: packages, endpoints, covered files and run state. Findings and verdicts never are.
    • SARIF revision provenance binds a report to the selected commit. --attach-binding caller-asserted records an explicit caller assertion for fully digested directories.
    • Per-attachment record limits degrade coverage instead of failing.
  • map route writes inert follow-up plans. map compare compares maps by stable identity; --format markdown gives a readable pull-request summary, and .github/actions/map-diff is a reusable action. map locate annotates SARIF locations with map ownership, with --source-uri and --uri-base for tools that emit container paths or undeclared bases.

Control and measurement

  • Presets:

    • balanced (the default);
    • low-memory (fewer workers, a smaller Git object cache, a lower per-file cap; about 60% less peak memory on the Linux kernel);
    • thorough (larger inventory and observation budgets).

    Every setting is typed and visible with map settings, and each is labeled performance-only or coverage-affecting.

  • --stats-json PATH writes deterministic cost counters and timings to a separate document, and --cpuprofile/--memprofile write Go profiles. The map document itself is byte-identical across worker counts, locations, locales and time zones.

Git reading

  • The binary excludes go-git's transport client, and a build contract test checks the linked executable. The embedded fork's go.mod still downloads unused SSH transport modules. Release binaries are about 11% smaller than 0.9.0, despite the map.
  • Git-mode peak memory on the Linux kernel dropped by about 35%.
  • SHA-256 object-format repositories and corrupt Git directories fall back to directory mode with a warning.

Distribution

  • Two command names. dirq is a shorter name for dircue: the same program, flags, exit statuses and machine-readable output, with help text showing the invoked name. Release archives include dirq (a symlink on Linux and macOS, a copy of the executable on Windows), wheels install both commands, an image built from the Dockerfile has both, and go install github.com/war-and-code/dircue/cmd/dirq@v1.0.0 installs dirq.
  • The module path is now github.com/war-and-code/dircue. The maintained Enry, go-git and scc snapshots are embedded in the module with recorded provenance, so no replace directives are needed.
  • Release archives for Linux, macOS and Windows (amd64 and arm64 where supported) come with SHA256SUMS and matching Python wheels.
  • Every release asset carries a GitHub SLSA build-provenance attestation (gh attestation verify <file> --repo war-and-code/dircue). SHA256SUMS is additionally signed with keyless Sigstore cosign; the bundle (SHA256SUMS.sigstore.json) is attached to the release. See README.md for exact verification commands.
  • Bulky evidence receipt files from prior performance and release runs are assets of the evidence-archive-1 prerelease; make fetch-receipts restores them. No test or CI job reads them from the working tree.

Evidence

  • Linguist parity. Across the 38-repository atlas, dircue matches Linguist 9.7.0 exactly on all 355 language totals. Against scc 4.1.0, 261,081 of 262,575 shared per-file counters are identical. Every remaining difference is a file where dircue follows its Linguist language to a different scc grammar, recorded per file (tests/atlas/results/1.0.0).

  • Map accuracy is measured per question against hand-written labels for seven repositories (GOLDEN.md). The labeled set also informed map development, so these results are not an independent evaluation. Every question meets the gate of precision ≥ 0.90 and recall ≥ 0.80:

    • components, deployables and capabilities: 1.00 / 1.00;
    • interfaces: 1.00 / 0.98;
    • relationships: 0.995 / 0.98 (216 matched, 1 extra, 4 missed).

    The map never claimed complete where the labels did not.

  • Holdouts labeled before dircue ran. Four repositories were labeled from source before any map run: OWASP BenchmarkJava and BenchmarkPython, AppFlowy editor (Dart) and AWS CardDemo (COBOL). The first run found 5 of 18 labeled facts. The map work they prompted finds 12 of 18 against those original labels, and 18 of 18 after the labels were restated in the map's documented vocabulary, with each change citing the source (tests/map_corpus/holdout_labels).

  • A fresh blind check. Four repositories (OpenMRS core, a Flask application, the bloc Dart monorepo and a Prisma/Express application) were labeled blind before the first candidate run (tests/map_corpus/fresh_labels). The preserved first-run receipt predates subsequent map and scorer changes; it is not validation of the current implementation. The original frozen-label receipt records relationship precision 0.38 and recall 0.88. Re-scoring the frozen labels with the revised scorer and map yields 0.425 / 1.00; this is regression evidence, not a second blind measurement. Source review found that most extra facts were true declarations the labels had omitted. After source-checked corrections, capabilities and relationships both score 1.00 / 1.00. Those corrected scores are also regression evidence, not an independent accuracy estimate. Procfiles remain a known gap (#146).

  • An independent source-first check. Two further pinned sources were labeled before dircue was run on them (tests/map_corpus/independent_labels). Within one fully enumerated Spring Boot Docker subtree, component and deployable precision and recall were both 3/3. The bounded Flask-on-Docker oracle and the other Spring categories support targeted recall only, and expose missed facts and naming disagreements. Those observations are retained in independent_results.json; this small sample does not establish whole-map or population accuracy.

  • Committed fixtures from pinned Syft, OWASP Noir, ruff and Semgrep exercise attachment, routing and location behavior (tests/syft-oracle, tests/tools).

  • A source-first review slice. Four more repositories (chi, FastAPI, changedetection.io and Umami) were labeled from source before the candidate ran on them, by the team that develops dircue (tests/map_corpus/review_holdout). On its first run the map matched 27 of 29 selected positive facts after vocabulary normalization (15 raw-exact) and contradicted none of the 10 scorable bounded negatives (2 fell outside their oracle scope). Source review later dropped the two misses, both Compose runs labels inferred from name similarity, as label errors; the first-run score remains the measurement. CI reproduces the score from the committed receipts.

  • Test strength. The suites include 14 metamorphic invariants, mutation-testing baselines, on-demand fuzz campaigns (including dircue's Git index reader) and executable regression checks from committed receipts.

Fixes

  • make fuzz-campaign runs each Go fuzz function for a configurable duration (FUZZ_TIME, default 60 s); accepts FUZZ_PKG and FUZZ_CACHE overrides.
  • pyproject.toml tool-only files (those with [dependency-groups] but no [build-system], runtime dependencies, or packaging layout) are treated as non-component manifests, consistent with how setup.cfg tool-only files were already handled.
  • Gemfile conditional gems: gems inside if/unless/case blocks, one-line blocks, trailing if/unless modifiers and optional: true groups are marked conditional. Gems in group blocks carry a gemfile-group:<names> condition; groups made only of development and test do not contribute capabilities, while other groups (such as production) still do.
  • --summary duplicate component names: components that share a name and ecosystem in sibling directories are labeled with their root path (e.g. api (modules/billing/api)) instead of being silently dropped; a solution and its same-named member project are still shown once.
  • Cargo auxiliary target roles: [[bench]] targets receive role: tooling, [[test]] integration targets receive role: test, and [[example]] targets receive role: example. The role property and its allowed values are documented in docs/MAP.md.
  • Map availability: a workflow, Kubernetes manifest or other configuration file whose name starts with readme, changelog or contributing (for example .github/workflows/changelog.yml) was treated as documentation, and the map aborted with no output. Only Markdown-family files and plain-text README, CHANGELOG and CONTRIBUTING files are documentation now. make corpus-availability requires every repository in a corpus to map successfully.
  • Go net/http client evidence: importing net/http no longer implies net:http-client; the file must reference a client API, and the evidence cites that line with basis code_syntax. net/http/pprof, httptest, fcgi and cgi imports never imply a client, httputil needs a reverse-proxy or client reference, and http.NewRequest alone is not client evidence.
  • Project attribution at ambiguous roots: a CodeUri or workflow working directory inside a directory with two projects was attributed to a broader ancestor project. It now stops at the nearest root and records ambiguous_component_root; a reference that resolves outside the repository (such as a root-level CodeUri: ..) is never attributed and records path_outside_repository. Workflow working directories created at run time, such as a checkout that names a repository: or a git clone target, were attributed to the enclosing project; they now record named_repository_checkout or path_not_in_repository, while a directory inside a checkout of this repository maps back to its repository path.

Compatibility

  • Against the published 0.9.0 executable, 227 of 278 compatibility cases produce identical stdout and stderr; 51 have output changes, with no exit-status changes. Eighteen cases expose a new warning when an unborn Git repository falls back to directory mode in structured analysis. Eighteen add a Gradle root name from settings.gradle.kts. Fifteen reflect declaration changes: wider ecosystem support, Maven names, uv manifest classification, and removal of Go-version and npm developer-task interfaces. Strict raw Linguist output matches for committed trees, ordinary directories and unborn repositories. See the comparison receipt.
  • dircue compare (saved profiles) and dircue map compare (maps) reject each other's documents with a pointer to the right command.

Upgrading from 0.9

  • Most tested invocations retain their output and exit status; see the compatibility results above for the 51 cases whose output changed. Importers of pkg/ packages must use the qualified path github.com/war-and-code/dircue/pkg/....
  • Install with go install github.com/war-and-code/dircue@v1.0.0, and go install github.com/war-and-code/dircue/cmd/dirq@v1.0.0 for dirq.