The following is a Claude generated plan. Plans are created using markdown and the exact file contents of the plan are pasted below, no changes needed since Discourse can display the markdown.
The plan was created in VScode with Claude Code for VS Code
Model: Opus 4.5
Plan mode: active
Thinking: On
In my workflow after the changes are working as expected, including changes needed that were not in the plan, I run a skill that checks my current local skills and if anything was learned that may be of value as a new skill or addition to existing skills will create a plan to add/update the local skills.
Thus Claude learns new skills from my mistakes, fixing overlooked details, etc.
Plan: Add Directive Filtering to prolog_coverage_ai
Summary of Changes
| File | Change |
|---|---|
lib/prolog_coverage_ai.pl |
Rename module, add skip_directive_clauses/1 option, modify line_annotation |
scripts/run_coverage_tests.pl |
Import local module, use new option |
Goal
Create a local copy of prolog_coverage.pl renamed as prolog_coverage_ai module, with a new option to filter out ### (uncovered) annotations for persistent/1 directives.
Problem Analysis
The persistent/1 directive uses term expansion to generate multiple predicates at compile time. The coverage system sees:
- The directive itself - marked as
###(never “executed” at runtime) - The generated predicates - these ARE executed and show
++Ncounts
The ### is misleading since directives aren’t runtime-executed code.
Call Chain for ### Annotations
show_coverage/1 (line 280)
└─ file_coverage/3 (line 313)
└─ file_details/4 (line 427)
└─ detailed_report/4 (line 723)
└─ line_annotation/4 (line 745) ← Creates ### annotation
Key code at line 745-748:
line_annotation(File, uncovered, Clause, Annotation) :-
!,
clause_or_set_source_location(Clause, File, Line),
Annotation = (Line-ansi(error,###))-3.
Implementation Plan
Step 1: Rename Module
- Change module declaration from
prolog_coveragetoprolog_coverage_ai - File:
lib/prolog_coverage_ai.pl
Step 2: Add New Option
Add option skip_directive_clauses(+Predicates) where Predicates is a list like [persistent/1].
2a. Update predicate_options declaration (~line 138):
:- predicate_options(show_coverage/1, 1,
[ ...existing options...,
skip_directive_clauses(list) % NEW
]).
2b. Update module documentation (~line 256, after the annotation table):
Add to the show_coverage/1 documentation:
% - skip_directive_clauses(+Directives)
% A list of Directive/Arity terms. Clauses whose source line
% contains a matching directive (e.g., `:- persistent(...)`) will
% not be marked with `###` even if uncovered. This is useful for
% term-expanding directives like persistent/1 where the directive
% itself is not runtime code but generates runtime predicates.
% Example: `skip_directive_clauses([persistent/1])`
Step 3: Modify line_annotation/4 (line 745)
Add a clause that checks if we should skip this uncovered clause:
line_annotation(File, uncovered, Clause, Annotation, Options) :-
option(skip_directive_clauses(Directives), Options, []),
Directives \== [],
clause_is_directive(Clause, File, Directives),
!,
fail. % Skip this clause - no annotation generated
line_annotation(File, uncovered, Clause, Annotation, _Options) :-
!,
clause_or_set_source_location(Clause, File, Line),
Annotation = (Line-ansi(error,###))-3.
Step 4: Add Helper Predicate clause_is_from_directive/3
%% clause_is_from_directive(+Clause, +File, +Directives) is semidet.
% True if Clause's source line matches a directive pattern.
% Directives is a list like [persistent/1, discontiguous/1].
clause_is_from_directive(Clause, File, Directives) :-
clause_or_set_source_location(Clause, File, Line),
catch(file_line_text(File, Line, LineText), _, fail),
member(Directive/_Arity, Directives),
format(atom(Pattern), ':- ~w(', [Directive]),
sub_atom(LineText, _, _, _, Pattern).
%% file_line_text(+File, +LineNo, -Text) is det.
% Read specific line from file. Uses file_text/2 already in module.
file_line_text(File, LineNo, Text) :-
file_text(File, String),
split_string(String, "\n", "", Lines),
nth1(LineNo, Lines, Text).
Note: The file_text/2 predicate already exists in the module (line 888) for reading file content.
Step 5: Thread Options Through Call Chain
Predicates needing modification to pass Options:
- detailed_report/4 (line 723) - already has Options, passes to line_annotation
- line_annotation/4 (line 745) - change to line_annotation/5 with Options param
The convlist/3 calls at lines 726-727 need updating:
% Before:
convlist(line_annotation(File, uncovered), Uncovered, Annot1),
% After:
convlist(line_annotation(File, uncovered, Options), Uncovered, Annot1),
Note: convlist/3 uses a goal with arity N, calling it with N+2 args. So line_annotation/5 works as line_annotation(File, uncovered, Options) + 2 args from convlist = 5 args total.
Step 6: Update run_coverage_tests.pl
Change import and add new option:
:- use_module('lib/prolog_coverage_ai').
% In show_coverage_safe/1:
show_coverage([color(false), skip_directive_clauses([persistent/1]) | Options])
Files to Modify
lib/prolog_coverage_ai.pl- Main changes (rename + new filtering)scripts/run_coverage_tests.pl- Use local module with new option
Testing
Run: swipl scripts/run_coverage_tests.pl 2>&1 | tee coverage_test_results.log
Verify: cov/introspection_engine.pl.cov no longer shows ### for persistent/1 lines
Here is an example of things I missed in the plan or just in situational awareness
- Noting that the test take 10 minutes to run, so in inspecting the command added as a reply.
- Forgot to check my token usage which ran out in the middle of running the tests. Normally I will not reply which leaves the process ready to go once the limit resets. Resuming a session for token limit reached can sometimes use 5% of all tokens, ouch.
-
Should have noted in the plan that I would manually run the test and check the results. Doing so stops the AI from wasting tokens in a loop checking for the test to end.
-
Should have switched the LMM model from Opus 4.5 to the cheaper model Sonnet 4.5 which is easily capable of doing the code changes and using a lot less tokens and turning thinking off.
In checking the cov file to verify that the ### are filtered out for persistent/1 directives
90 ++20 ⮰
++4,727 ⮰
++21,486 ⮰
+55,950-20 :- persistent(pred_def(name:atom, arity:integer, module:atom, file:atom, line:integer)).
the code works as expected.
However since my knowledge of all of the places where this specific case would be of use more generally, or adjusted as needed, I do not plan to submit a PR.
Also since there is nothing that would be new as a Claude skill will skip running the check for possible skills.
prolog_coverage_ai.pl (45.8 KB)

