Trailmark structural analysis
Contract
| Field | Bound contract |
|---|---|
| Trigger | A target needs a quick structural overview (languages, entrypoints, dependencies) before deeper analysis, or a detailed single-snapshot analysis (hotspots, entrypoints, coarse taint, blast radius, privilege boundaries, proxies, subgraphs, available type references). |
| Authority | Read-only: reads the target source tree and writes nothing; there is nothing to roll back. Never install, upgrade, or clone trailmark or any dependency. No remote mutation. |
| Side effect | Mode summary emits the language list and summary output. Mode full emits a full JSON structural-analysis payload. |
| Done | Mode summary: detected languages, Entrypoints:, and Dependencies: are all present in the returned report, or an installation or language gap is reported. Mode full: languages, summary, attack surface, hotspots, proxies, all named subgraph counts, available edge/type details, and empty-pass results are returned without fabrication. |
Inputs
- Mode:
full(default) orsummary.summaryreturns detected languages, entrypoints, and dependencies without running the query engine. - Target directory path. Required; supplied by the invoker with no default.
- Trailmark installed in the environment. Required; the user installs it themselves.
Refusals
- Will not install, upgrade, or clone Trailmark or any dependency.
- Will not substitute manual analysis when Trailmark imports fail.
- Will not fabricate missing payload sections or treat an empty pass as failure.
- Will not assume a version-gated method exists without
hasattr()proof. - Will not widen a summary run into full structural analysis, hotspot scores, or taint data.
Procedure
Validate the target at its trust boundary: confirm the supplied path exists and is a readable directory. If not, report the invalid target and stop; never probe a guessed path. Done when: the target is confirmed as a readable directory.
Check that trailmark is available. Verify the
trailmarkcommand is on PATH or thetrailmarkPython module is importable;uv run trailmark analyze --helpis an acceptable fallback probe. If every check fails, report "trailmark is not installed" and return. Do not runpip install,uv pip install,git clone, or any install command. Optionally record the version; do not fail if the version command is missing. Use API feature probes in step 4 instead. Done when: Trailmark availability is confirmed or the installation gap is reported.command -v trailmark >/dev/null 2>&1 || \ python3 -c "import trailmark" 2>/dev/null || \ uv run trailmark analyze --help >/dev/null 2>&1trailmark --version 2>/dev/null || trueDetect languages with Trailmark's parse API. Run the snippet below with
python3; if thetrailmarkmodule is not installed, rerun the same snippet withuv run --with trailmark python3 -. If the import still fails, report the exact import failure and return; do not install. If the result is[], report "Trailmark found no supported languages under target" and return. Done when: supported languages are detected or the language gap is reported.python3 - "{args}" <<'PY' import json import sys try: from trailmark.parse import detect_languages # canonical location since 0.3.x except ModuleNotFoundError: # v0.2.x predates trailmark.parse; the same function lives in query.api from trailmark.query.api import detect_languages print(json.dumps(detect_languages(sys.argv[1]))) PYRun the mode's analysis.
- Mode summary: run
trailmark analyze --language auto --summary <target-directory> 2>&1, falling back touv run trailmark analyze --language auto --summary <target-directory> 2>&1. Run only this summary pass; do not widen into full structural analysis, hotspot scores, or taint data. Verify the output includes the detected languages from step 3, anEntrypoints:line, and aDependencies:line; if any is missing, report the specific missing field and stop. Done when: the summary output is captured with all three fields. - Mode full: run the full structural analysis via
QueryEnginewithpython3; if the import fails, report the exact import failure and return; do not install. The snippet builds a graph, runsengine.preanalysis()(all four pre-analysis passes), and assembles the payload with version-gated feature probes. Probe v0.4-only methods withhasattr()before querying them. Done when: the full JSON payload is assembled or the exact import failure is reported.
python3 - "{args}" <<'PY' import json import sys try: from trailmark.parse import detect_languages # canonical location since 0.3.x except ModuleNotFoundError: # v0.2.x predates trailmark.parse; the same function lives in query.api from trailmark.query.api import detect_languages from trailmark.query.api import QueryEngine target = sys.argv[1] languages = detect_languages(target) engine = QueryEngine.from_directory(target, language="auto") preanalysis = engine.preanalysis() def summarize_subgraph(name: str, limit: int = 25) -> dict[str, object]: nodes = engine.subgraph(name) summary = { "count": len(nodes), "sample_ids": [node["id"] for node in nodes[:limit]], } if hasattr(engine, "subgraph_edges"): summary["edge_count"] = len(engine.subgraph_edges(name)) return summary graph = json.loads(engine.to_json()) nodes = graph.get("nodes", {}) proxy_nodes = [ node_id for node_id, node in nodes.items() if node.get("kind") == "proxy" or node.get("origin") == "proxy" ] payload = { "languages": languages, "summary": engine.summary(), "preanalysis": preanalysis, "attack_surface": engine.attack_surface()[:25], "hotspots": engine.complexity_hotspots(10)[:25], "proxy_nodes": proxy_nodes[:25], "subgraphs": { name: summarize_subgraph(name) for name in engine.subgraph_names() }, } if hasattr(engine, "type_references"): payload["type_reference_samples"] = { node_id: engine.type_references(node_id)[:10] for node_id in list(nodes)[:25] } print(json.dumps(payload, indent=2)) PY- Mode summary: run
Verify the output. Mode summary: the report carries the language list, the full summary output with its
Entrypoints:andDependencies:lines, and the version when captured. Mode full: the payload must includelanguages,summary,preanalysis,hotspots(possibly empty),proxy_nodes(empty on v0.2.x or when there are no unresolved calls; on 0.5.0+ may includeproxy.external:*entries declared in.trailmark/links.toml), andsubgraphswith counts and sample IDs. On Trailmark 0.5.0+,attack_surfaceentries may carry anattributesobject; pass it through unchanged. Some subgraphs may have zero nodes; this is normal. Return the full output regardless. Done when: every required field for the mode is present and empty sections remain explicit.
Failure and recovery
- Invalid target: the supplied path does not exist or is not a directory. Report and stop; never probe a guessed path.
- Trailmark not installed: report "trailmark is not installed" and return. Do not install, upgrade, or clone anything.
- No supported languages detected: report "Trailmark found no supported languages under target" and return.
- Import fails under
python3and theuv run --with trailmarkretry: report the import error and return. Do not attempt manual analysis as a substitute; manual analysis misses what tooling catches. - Missing summary field: report the specific missing field; a partial summary never satisfies Done.
- Empty pass output: some passes produce no data for some codebases (e.g., no privilege boundaries). Return the full output regardless; empty is not failure.
- v0.4-only method absent: users may have Trailmark 0.2.x installed. Probe with
hasattr()before querying version-gated methods. Never assume a v0.4 field is always present. - Partial-result rule: return whatever the engine produced up to the failure point. Do not fabricate missing sections. Never swallow errors or pretend the done predicate holds.
Output
- Mode summary: a report containing the detected language list, the full
trailmark analyze --language auto --summaryoutput including itsEntrypoints:andDependencies:lines, and the trailmark version in the metadata when captured; or a terminal gap classification:trailmark is not installed,Trailmark found no supported languages under target, or the named missing-field gap. - Mode full: a JSON payload ordered as languages, summary, preanalysis, attack_surface, hotspots, proxy_nodes, subgraphs, then type_reference_samples when supported; empty sections remain explicit and no missing evidence is fabricated.