Skip to content

Fix/json output pollutions readme blank lines - #11

Merged
jier merged 20 commits into
mainfrom
fix/json-output-pollutions-readme-blank-lines
Sep 13, 2026
Merged

jier merged 20 commits into
mainfrom
fix/json-output-pollutions-readme-blank-lines

Conversation

@jier

@jier jier commented Sep 13, 2026

Copy link
Copy Markdown
Owner

feat: RoleExecutionGraph, collection support, graph-authoritative complexity

Stress tested repository on existing repositories

Category Anchor candidate(s)
Simple role geerlingguy/ansible-role-docker, geerlingguy/ansible-role-nginx
Modular role (task files split by concern) nginx/ansible-role-nginx, geerlingguy/ansible-role-mysql
Collection-based project (galaxy.yml) prometheus-community/ansible, dev-sec/ansible-collection-hardening
Handler-heavy (many handlers / notify chains) ansible-lockdown/UBUNTU22-CIS (40 handlers, 139 notify lines)
Include-heavy (include/import boundaries) ansible-lockdown/UBUNTU22-CIS (96 boundaries, 68 task files)
Condition-heavy (when, blocks) ansible-lockdown/UBUNTU22-CIS (826 when lines, 189 blocks), openstack/ansible-hardening
Loop-heavy (loop, loop_var, with_*) geerlingguy/ansible-role-mysql, dev-sec/ansible-collection-hardening
Dynamically structured (templated includes, include_role) nginx/ansible-role-nginx, openstack/ansible-hardening, prometheus-community/ansible
Graph too large for a default Mermaid view ansible-lockdown/UBUNTU22-CIS (~925 tasks)

(summarizing the arc: output fixes → RoleExecutionGraph → collection support/parity → loop_control → graph bounding → perf → authoritative boundary counts )

jier added 20 commits September 13, 2026 00:49
- Terminal success message now receives real recommendations, so it no longer claims “no enhancement opportunities” after warnings.
- license (BSD, MIT) now renders as BSD, MIT.
- Multiline YAML defaults now show normalized source content instead of <multiline value: ...> placeholders.
RoleExecutionGraph
- Added docsible/graphs/role_execution.py.
- Public seam: build_role_execution_graph(role_info).
- Models role, task-file, task, handler, variable, and external-role nodes.
- Models typed include/import, role-call, notify, variable-use, and containment edges.
- Preserves static, dynamic, unknown, and unresolved_external resolution.
- Uses raw loaded Ansible tasks, so no second parser or NetworkX dependency.
README projections
- Execution phases now traverse static relationships from tasks/main.yml.
- Nginx now starts at main.yml, shows seven OS branches with their actual conditions, then vhosts.yml.
- Variable and handler diagram edges are source-backed.
- Removed the remaining first-file variable and last-file handler proxy behavior during normal rendering.
1. Loop metadata
- Ordinary task nodes now retain loop, with_items, with_first_found, etc.
- README task tables show a conditional Loop column.
- MySQL now displays all legacy loops.
- Commit message: feat: render task loop metadata
2. Graph-derived complexity
- Structural metrics remain unchanged.
- Added graph metrics: static reachable files, dynamic/unknown boundaries, external roles, loop tasks, notification edges, and orphan files.
- MySQL reports 10 reachable files, 11 loop tasks, 2 notification edges, 0 orphans.
- Commit message: feat: add execution graph complexity metrics
3. Execution routes
- Replaced placeholder “Phase” prose with source-backed Execution Routes.
- Shows entry point, static continuation, conditional paths, and unreachable files without pretending branches all execute.
- Commit message: feat: replace phase placeholders with execution routes
4. Role boundaries and renderer contract
- Static import_role targets become explicit external-role reference nodes.
- Dynamic include_role targets remain unresolved/dynamic.
- RoleExecutionGraph.to_dict() is JSON serializable and tested as the renderer contract.
- Commit message: feat: expose role execution graph boundaries
- Resolves only {{ role_path }}/tasks/<literal> into a static task-file boundary.
- Leaves every other templated target dynamic.
- Prevents safe nested includes from becoming false orphans.
- Enterprise projection policy:
- Detailed task-file architecture budget:
- max 20 files
- max 35 boundaries
- max fan-out 8
- Exceeding any signal selects a grouped directory-level execution overview.
- The full graph remains intact; only the README projection changes.
- Dynamic/unknown source groups and handler relationships remain visible.
- Enterprise routes collapse individual orphan files into one summary count.
- Enterprise graph availability:
- ENTERPRISE roles now receive graph summaries through smart defaults.
- Explicitly disabling smart defaults still suppresses graphs, preserving the existing contract.
- Resolves safe {{ role_path }}/tasks/<literal> paths as static.
- Resolves templated paths with a literal filename prefix, such as install-{{ os_family }}.yml, into multiple dynamic candidate edges.
- Keeps unconstrained expressions like {{ unknown }}.yml dynamic without inventing candidates.
- Dynamic candidate files are no longer false orphans.
- ENTERPRISE output uses the grouped execution overview under the agreed readability budget.
- Dynamic candidates are summarized, not listed one-by-one.
- Explicitly disabling smart defaults remains graph-free.
Official nginx recalibration
- Dynamic boundaries: 22
- Unknown boundaries: 0
- Statically reachable task files: 15
- False orphans: 0
- complexity: existing structural metrics plus:
- collection_dependencies
- conditional_decision_points
- static reachable task files
- dynamic/unknown boundaries
- loop tasks
- notification edges
- orphan task files
- execution_graph: complete serialized graph nodes and edges.
Standard README order is now:
Overview
Architecture Overview
Execution Graph Summary
Execution Routes
Recommendations
Variable Reference
Task File Reference
Handlers
docsible/templates/collection/sections/overview.jinja2 — removed a literal \n typo printed after every author name; fixed loop whitespace control.
docsible/templates/collection/sections/galaxy_info.jinja2, dependencies.jinja2, plugin_list.jinja2 — added missing -%}/{%- trims so list items and conditional metadata blocks don't leak blank lines.
docsible/templates/collection/macros/repo_links.jinja2 — rewrote render_arguments_list (the argument-spec nested list renderer) with correct whitespace control; this was the single worst offender (4+ blank lines per option).
docsible/templates/collection/sections/roles_list.jinja2 — fixed the ## Roles heading list and per-role sections to stop emitting a blank line between every entry.
docsible/renderers/readme_renderer.py — render_collection() now runs MarkdownProcessor.process(), same as render_role(), capping any residual blank-line runs at 2.
…ty overview for collections

Complexity Overview: total roles, total tasks, and a category breakdown table (Enterprise/Complex/Medium/Simple counts) — factual aggregates, not a synthesized "collection complexity score."
Role Index: one row per role — name (linked), complexity badge, task count, critical/warning counts, top finding — sorted by complexity descending (ties broken alphabetically), so the roles needing attention surface first without opening anything.
- document_collection_roles() and my dry-run now iterate ProjectStructure.find_roles() (the same filter scan uses) instead of os.listdir/iterdir of roles/*.
- New _role_less_dirs + _warn_role_less_dirs: role-less dirs (uninitialized submodules) are skipped, warned, and excluded from the dry-run count and the collection Role Index — and never written into.
feat: capture loop_control and render custom loop variables
- Shared extract_loop_control() in special_tasks_keys (→ loop stays the keyword; loop_var/index_var/label captured; pause intentionally excluded).
- Graph task-node metadata now carries loop_control; serialized into to_dict().
- README Loop column renders loop (as: <var>) in both standard + hybrid templates.
…medium code base and not on enterprise graded project.
docsible/diagrams/types/architecture.py — grouped overview now caps visible groups (_MAX_GROUP_NODES = 10): keep entry point + largest groups by task count, fold the tail into one other (N task files) node (hub fan-out collapses via existing edge dedup). Plus label pluralization fix (1 task file vs N task files).
…actor: build the RoleExecutionGraph once per command and thread it.
…ngle source of truth)

role_analyzer.analyze_role_complexity: task_includes/role_includes are now derived from the RoleExecutionGraph (distinct source tasks of its include edges), replacing the separate flattened-task regex that never matched bare include:.
Two complexity fixtures updated to carry a production-faithful mermaid list (they previously had only processed tasks, which the graph doesn't read — that's why they initially broke). New regression test locks "legacy include: counted + graph-authoritative."
@jier
jier merged commit df03077 into main Sep 13, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant