Report Tech Debt Workflow Design¶
Workflow and domain design for the tech-debt loop.
| Layer | Document |
|---|---|
| Platform | Multi-Branch Loops Design |
| Caller shell | Loop Caller Workflows Design |
| Invariants | Loop Engineering Design |
Artifacts: on-loop-tech-debt.yaml · skill tech-debt · tech-debt/scripts/detect_tech_debt.sh
Shared caller keys: Loop Caller Inputs Reference.
Purpose¶
Run a full-repository mechanical technical-debt scan, classify findings via the skill taxonomy, and open an L2 merge-gated PR that writes a dated report under docs/report/tech-debt/.
Supported use cases¶
- Weekly cron scan of
mainfor mechanical debt signals (shell, Go, Terraform, workflow, dependency sensors) - Classify
signals[]andhotspots[]into prioritized findings with evidence - Write
docs/report/tech-debt/YYYY-MM-DD.mdvia L2 review PR - Compare against
previous_reportfor resolved, recurring, and regression items
Out of scope¶
- Code fixes, refactors, or dependency upgrades (report-only loop)
- GitHub Issue creation at any level (log + state + report PR only)
- L1 log-only observation phase (skipped — L2 from start)
- CVE database lookups or duplicate lint enforcement already covered by CI
- Hook-triggered or user-invoked debt scans
- Loop state and detect script management
Report loop family¶
Report loops emit structured artifacts under docs/report/<domain>/ via merge-gated PRs (dogfood: loop_name: tech-debt, skill tech-debt).
Action loops (docs-updater, ci-sweeper, refactor) modify application or documentation source to fix drift or failures. Report loops classify mechanical signals and publish reports only — they do not edit source outside the report allowlist.
| Loop | Skill | Role | Trigger |
|---|---|---|---|
tech-debt |
tech-debt |
Cron loop: detect signals + skill classify/report | on-loop-tech-debt.yaml |
docs-updater |
docs-updater |
Action loop: doc drift detect + fix PR | on-loop-docs-updater.yaml |
ci-sweeper |
ci-sweeper |
Action loop: CI failure detect + fix PR | on-loop-ci-sweeper.yaml |
refactor |
refactor |
Action loop: H1 structural refactor fix PR | on-loop-refactor.yaml |
Detect script path: tech-debt/scripts/detect_tech_debt.sh (under common package).
Skill execution boundaries: tech-debt SKILL.md (USE FOR / DO NOT USE FOR).
Modes¶
| Mode | Default | Behavior |
|---|---|---|
integration |
on | Detect on watch branch → report PR to same branch |
pull_request |
off | not supported for this loop |
Caller inputs¶
Keys are passed in on-loop-tech-debt.yaml via with: on ci-loop-caller.yaml (alphabetically ordered). Multiline values (agent_checker_instructions, agent_maker_instructions) are defined inline in the caller workflow.
Shared semantics: Loop Caller Inputs Reference. Platform branch/finalize caps: canonical table.
Schedule: 0 8 * * 1 (Monday 08:00 UTC, weekly).
| Input / JSON key | Description | Dogfood value |
|---|---|---|
agent_maker_max_turns |
Max maker agent turns per loop attempt (one Agent→Verify cycle). | 100 |
agent_maker_model |
Maker model ID. Cursor: agent --list-models. |
claude-sonnet-5 |
agent_maker_effort |
Maker reasoning effort. Only engines with an effort flag (claude) consume it | medium |
agent_loop_max_attempts |
Max Agent→Verify retry cycles before finalize records failure. | 3 |
agent_checker_instructions |
Checker APPROVE/REJECT rubric. Allowlisted paths only; closed-set fixes (broken_doc_ref, stale_doc, pin_drift) on docs/manifests; no invented paths; cap Critical+High at 25. |
Inline in caller workflow |
agent_checker_max_turns |
Max checker agent turns per verification. | 100 |
agent_checker_model |
Checker model ID. Cursor: agent --list-models. |
claude-opus-5 |
agent_checker_effort |
Checker reasoning effort. Only engines with an effort flag (claude) consume it | medium |
allowlist |
Comma-separated globs the maker may modify. Report path plus optional secondary closed-set fix paths (docs/**/*.md, package.json, go.mod). |
docs/report/tech-debt/**/*.md,docs/**/*.md,package.json,go.mod |
branch_match |
Comma-separated integration branch patterns to watch. | main |
branch_state |
Branch for .loop/* persistence, state migration, and watch fallback. |
main |
budget_max_runs_per_day |
Daily run cap keyed by loop_name. Caller input; .loop/loop-budget.json overrides when present. |
2 |
budget_max_tokens_per_day |
Daily aggregated token cap across loops. | 1000000 |
denylist |
Comma-separated globs the maker must not touch. | **/.env,**/credentials*,**/secrets*,src/**,.github/** |
detect_script |
Domain detect script path. | .agents/skills/tech-debt/scripts/detect_tech_debt.sh |
engine |
AI engine (claude, copilot, codex, cursor). Maps AGENT_TOKEN to engine env. |
claude |
environment |
GitHub Environment for env-scoped secrets inside the reusable workflow. Leave default when repository secrets suffice. |
default |
delivery |
Platform delivery after APPROVE (open_pr for dogfood). |
open_pr |
infer_files_pattern |
Extended regex to infer file paths from checker text. | See caller workflow |
level |
Autonomy level (L1, L2, L3). L2 from start — no L1 phase. |
L2 |
loop_name |
Loop identifier; state file .loop/state-tech-debt.json. |
tech-debt |
max_targets_per_schedule |
Max targets per cron tick after priority filters. | 1 |
may_edit |
Agent worktree edit gate (true for dogfood). |
true |
no_changes_verdict |
APPROVE or REJECT when maker produces no file diff. REJECT when signals present but no report file written. |
REJECT |
pr_body |
Optional static prefix (dogfood: ""). loop-finalize composes agent Overview/Summary + mechanical sections. See Loop PR Body Readable Design. |
"" |
pr_enabled |
Enumerate open PR heads. tech-debt uses integration branches only. | false |
pr_title |
PR title when finalize strategy is open_pr. |
docs(tech-debt): technical debt report (loop-tech-debt) |
agent_maker_instructions |
Domain instructions: classify signals; write dated report; compare previous report. | Inline in caller workflow |
agent_maker_skill_name |
Skill package to invoke. | tech-debt |
write_target |
Agent artifact when may_edit is true (report for this loop). |
report |
Domain detect environment (detect_domain_env_json)¶
Dogfood passes {} — defaults match the layout below. Override paths when changing report location (align allowlist / infer_files_pattern).
detect_domain_env_json: >-
{"TECH_DEBT_DIR":"docs/report/tech-debt"}
| JSON key / env var | Description | Dogfood value |
|---|---|---|
TECH_DEBT_DIR |
Report output directory | docs/report/tech-debt |
TECH_DEBT_LEGACY_SEARCH_DIRS |
Comma-separated prior-report search roots | docs/report/tech-debt |
TECH_DEBT_DATE_FORMAT |
UTC strftime for report basename |
%Y-%m-%d |
TECH_DEBT_FILE_EXTENSION |
Report filename extension (include dot) | .md |
TECH_DEBT_PREVIOUS_GLOB |
Glob for prior reports under search dirs | ????-??-??.md |
REPO_PATHS_EXTRA_PRUNES |
Optional extra detect prune roots (default: report parent) | unset |
Detect¶
Integration mode only¶
Per watch branch, loop-detect checks out the branch and invokes detect_tech_debt.sh with targets["integration:<branch>"].last_sha.
Detect script outputs mechanical signals (not semantic findings):
| Field | Role |
|---|---|
signals[] |
Mechanical debt indicators from repo-wide sensors |
hotspots[] |
Aggregated high-density paths or categories |
warnings[] |
Non-blocking sensor or scope warnings |
skip |
true when no debt signals warrant a run |
report_file |
Target path: <TECH_DEBT_DIR>/<UTC-date>.md (defaults under docs/report/tech-debt/) |
previous_report |
Latest dated report under TECH_DEBT_DIR plus optional TECH_DEBT_LEGACY_SEARCH_DIRS; empty string if none |
Default scope: full repository (sensors read source for evidence; docs/report/** excluded from sensors per skill references).
Skill (tech-debt) classifies signals into prioritized findings and writes report_file at L2.
loop-detect emits per-branch target_json:
from.ref= HEAD on watch branchto.branch= watch branchfinalize=open_pr
Stable filters (detect only)¶
- Circuit breaker on
targets[key].consecutive_failures - Budget (platform)
No infra/env classification — not applicable.
State fields (per target key)¶
| Field | Role |
|---|---|
last_sha |
Scan cursor; advances when report PR merges (on-loop-state-promote) |
pending |
Written at finalize on open_pr; promoted to last_sha on merge |
outcome |
pr-created, rejected, no-op, … |
consecutive_failures |
Circuit breaker |
No workflow_run_id / ci ledger.
Execute¶
- Worktree from
target.fromon integration branch - Checker diff baseline:
to.branch verifier_context: detect signal summary (counts, hotspots, warnings). Platform always wires; may be brief
Finalize¶
PR body is composed by loop-finalize from agent ## Overview / ## Summary (skill-owned) plus mechanical sections. Dogfood sets pr_body: "". See Loop PR Body Skill Contract.
Always open_pr to to.branch at L2. L3 push rarely appropriate for report loops; if enabled, requires explicit promotion review.
No domain_persistence_script.
Merge-gated cursor: Same platform rule as all L2 open_pr loops — pending at finalize, last_sha on report PR merge via on-loop-state-promote.yaml. See State delivery philosophy.
Skill¶
- Read
signals[],hotspots[],warnings[],report_file,previous_reportfrom detect JSON - Classify per skill
category-debt-taxonomy.mdandcommon-checklist.md - At L2, write full report to
report_filewithin allowlist - Emit session summary always (per
common-output-format.md) - Cap Critical + High findings at 25 per report
- Do not create GitHub Issues
Checker rubric outline¶
Inline in caller agent_checker_instructions (must match on-loop-tech-debt.yaml):
- APPROVE when all of the following hold:
- Changes stay under the allowlist (
docs/report/tech-debt/**/*.md,docs/**/*.md,package.json,go.mod) - Report content matches the provided detect signals and hotspots
- File paths cited in the report exist in the repository
- Critical + High findings count is at most 25
- No denylist paths were modified
- Closed-set fixes (
broken_doc_ref,stale_doc, simplepin_drift) only on allowlisted documentation or manifest paths
- Changes stay under the allowlist (
- REJECT when any of the following hold: no report file written despite non-empty signals; invented or non-existent paths in the report; changes outside the allowlist or denylist modifications; semantic claims unsupported by cited evidence; structural or security debt edited instead of reported
State delivery¶
See State delivery philosophy for platform rules.
Target (dogfood): merge-gated pending + on-loop-state-promote — same as changelog and docs-updater.
Persistence: state-tech-debt.json on branch_state via finalize inside ci-loop-agent.
Related action loops¶
refactor is an action loop that applies O1/O2 structural refactors via fix PRs — not a report-only loop. It may consume report findings as input context but belongs alongside docs-updater and ci-sweeper, not tech-debt. See Refactor Workflow Design.
Implementation Checklist¶
Shared platform: Multi-Branch — Shared platform checklist.
Loop-specific¶
- [x]
tech-debt/scripts/detect_tech_debt.sh(facts output) - [x]
on-loop-tech-debt.yamldogfood caller viaci-loop-caller - [x]
verifier_contexton execute path (build_verifier_context_from_result.signalsbranch) - [x] Skill consolidated into
common:tech-debt(wasloop-tech-debt/loop-report-tech-debt) - [x]
.loop/loop-budget.jsonentry fortech-debt