Guided dynamic workflow

Tell the agent what to do, not what to read.

deadpath workflow / MCP deadpath.workflow turns findings into an ordered checklist shaped by the repository's languages, frameworks and history. The first verify step is the judge's next check — the one artifact that would settle a hidden live path. It is data an agent executes, not prose it interprets. The model, if a key is set, is counsel for the defense over that packet — never a second scanner.

1

verify

Targeted grep and bounded reads. Never "read the repo".

2

edit

Propose a reviewable patch. Deletion is proposed, never performed.

3

validate

Compiler, type checker, tests for the detected stack; then re-scan.

4

remember

Record keep / false_positive / resolved so the next session starts warm.

Why stages 1–3 stay offline

Putting a model in the graph or the judge looks like “more reasoning.” Buyers who have been burned by AI dead-code tools will not buy it:

Stage four is where a model does add value: reading the judge's packet and trying to save code that is about to be proposed for deletion. Skipping block/remove used to look cheaper. That was the wrong safety story — those are the deletions that hurt if the regex judge missed a Temporal workflow or a house-style entry.

Optimal LLM use, by construction

Most "AI dead-code" tools stream the whole finding list — or the whole repository — through a model. Deadpath inverts that. The deterministic engine builds the case file; the model is a veto-only second devil's advocate over that file, asked once, remembered.

remove

Counsel first

Judge remove (usually scan block) is the highest blast radius. The model may confirm, escalate to verify, or overturn to keep. It cannot invent files.

verify

Counsel once, batched

Ambiguous findings go in the same call, at most 8 per run, as compact packets: id · kind · path · symbol · confidence · signals · why · judge. No file bodies. The model cannot upgrade these to likely_dead.

keep / note

Never sent

keep already has file:line evidence. note is too thin for a model to add anything but confabulation. Both stay in the report for humans.

Cached by evidence digest. Every verdict is stored in .deadpath/memory.json under a hash of the finding's path, symbol, signals, confidence and judge verdict. If nothing about the evidence changed, the model is not asked again — across sessions, agents and machines that share the memory file.

Offline identical. Without DEADPATH_API_KEY, triage returns deterministic heuristic verdicts from the same signals (framework/entry signal → keep; string/dynamic signal → verify; clean graph → likely_dead). Workflows have the same shape either way.

How verdicts shape the workflow

VerdictOrderingBudget hint given to the agent
likely_deadFirst (confirmed remove, after security_first)"A single grep is enough; skip reading the file unless the grep hits."
verifyAfter likely_dead"Read the grep hits, not the whole file."
keepRemoved from selected_findings, listed in kept_by_triage"Skip these; record them with deadpath.remember if you agree."

How it adapts to the stack

DetectedWorkflow changes
PythonImport-check touched packages with python -c "import pkg"; run pytest; run mypy/pyright if configured in pyproject.toml.
Python + framework (Django, Flask, FastAPI, Celery, SQLAlchemy, Click, Airflow, pytest)Adds verify steps for framework wiring (INSTALLED_APPS, router includes, autodiscover, entry points) and decorator registration.
TypeScript / JavaScriptReads package.json main/exports/bin and tsconfig.json; runs npx tsc --noEmit when TypeScript is a dependency; runs vitest or jest if present.
Go · Rust · Java · Kotlin · Scala · C# · Ruby · PHP · Swift · Dart · Elixir · C/C++ · Lua · PerlAdds a verify step to check build wiring and DI/reflection registration, and language-specific validation: go build ./... && go test ./..., cargo check && cargo test, ./gradlew test, dotnet build, bundle exec rspec, phpunit, swift test, flutter test, mix test, ctest, busted, prove…
Monorepoprofile.monorepo: true so the agent scopes verification to the workspace and checks cross-package imports.
Memory has previous runsNew findings first. The context step states how many findings were already triaged so the agent does not re-read them.

Shape

{
  "auto_delete": false,
  "profile": { "primary": "py", "frameworks": ["fastapi", "pytest"], "monorepo": false, ... },
  "policy": { "block_threshold": 0.85, "warn_threshold": 0.55,
              "delete_requires": "explicit user approval after validation steps pass" },
  "llm": { "policy": "model is a veto-only counsel on judge remove (first) and verify/warn packets; cannot strengthen verify to likely_dead; keep and note are never sent; verdicts cached by evidence digest; never file bodies",
           "enabled": true, "called": true, "asked": 4, "cached": 0, "heuristic": 0, "skipped_keep": 1, "skipped_note": 0, "reviewed_remove": 3, "reviewed_verify": 1 },
  "selected_findings": ["orphan_file:pkg/orphan.py", "unused_export:pkg/exports.py:dead_symbol", "unused_export:pkg/hooks.py:maybe_dead"],
  "kept_by_triage": [],
  "steps": [
    { "id": "ctx-01",     "phase": "verify",   "action": "Read only the files named in this workflow...", "budget_hint": "First run for this repository; all findings are new." },
    { "id": "verify-02",  "phase": "verify",   "finding_id": "orphan_file:pkg/orphan.py", "grep": "pkg.orphan", "read": ["pkg/orphan.py"] },
    { "id": "verify-06",  "phase": "verify",   "finding_id": "unused_export:pkg/hooks.py:maybe_dead", "grep": "maybe_dead",
                          "triage": "verify: module imported whole; grep attribute access before acting",
                          "budget_hint": "Triage flagged a dynamic/string reference: read the grep hits, not the whole file." },
    { "id": "edit-03",    "phase": "edit",     "finding_id": "orphan_file:pkg/orphan.py", "action": "If verified: propose deleting ... Do not delete without user approval." },
    { "id": "validate-08","phase": "validate", "command": "python -c \"import pkg\"" },
    { "id": "validate-09","phase": "validate", "command": "pytest -q" },
    { "id": "validate-10","phase": "validate", "tool": "deadpath.scan" },
    { "id": "mem-11",     "phase": "remember", "tool": "deadpath.remember" }
  ]
}

CLI

deadpath workflow [PATH] [--format json|md|table] [--max 12] [--no-triage] [--no-llm]
deadpath triage   [PATH] [--format json|md|table] [--max-items 8] [--no-llm]
deadpath judge    [PATH] [--format table|md|json]