fix: stop verify, reports and briefs from claiming more than pipe checked #1

Merged
trevon merged 12 commits from fix/verify-soundness-and-injection into main 2026-09-22 18:24:06 +00:00
4 changed files with 77 additions and 5 deletions
Showing only changes of commit e2d84f5e75 - Show all commits

fix(guide): persist the yams report with its caution and provenance

`guide --yams` persists the markdown report, not the brief, and the markdown
had neither the brief's caution line ("static reachability claims ... confirm
each against the cited file") nor the [severity/confidence] of each finding -
so a report read back from the corpus presented every finding as equally
certain, with no framing. Its metadata still carried a stale `owner=pi` and
nothing to date or reproduce it.

The markdown now opens with the caution line, headings read
`### N. [severity/confidence] rule — location`, and the metadata records
`generated=<ISO-8601 UTC>` and, when the project is a git checkout,
`commit=<HEAD>`. `owner=pi` is gone.
trvon 2026-09-22 11:56:28 -06:00
Signed by: trevon
GPG key ID: 38686EA797D7A8A7

View file

@ -9,10 +9,12 @@ export 'src/guide.dart'
buildGuidance,
emitToYams,
findYams,
gitHead,
rankFindings,
ruleEpithet,
ruleFix,
ruleGlossary;
ruleGlossary,
yamsMetadata;
export 'src/index.dart'
show
DiagnosticRecord,

View file

@ -748,9 +748,11 @@ class GuideCommand extends Command<int> {
final error = await emitToYams(
name: name,
content: guidance.markdown,
metadata:
'task=pipe-guide,phase=report,owner=pi,source=analysis,'
'project=${p.basename(project.root)}',
metadata: yamsMetadata(
project: p.basename(project.root),
generated: DateTime.now(),
commit: await gitHead(project.root),
),
collection: 'pipe',
);
if (error != null) {

View file

@ -515,9 +515,13 @@ Guidance buildGuidance({
}
// --- markdown ----------------------------------------------------------
// The markdown is what `--yams` persists, so it carries the same caution as
// the brief: read later from the corpus, it has no other framing.
final markdown = StringBuffer()
..writeln('# pipe report — $root')
..writeln()
..writeln(cautionLine)
..writeln()
..write(coverageLine == null ? '' : '$coverageLine\n\n')
..writeln(
'${flow.unions.length} blocs/cubits, ${summary['stateVariants']} state '
@ -529,7 +533,10 @@ Guidance buildGuidance({
for (var i = 0; i < ranked.length; i++) {
markdown
..writeln()
..writeln('### ${i + 1}. ${ranked[i].rule} — ${ranked[i].location}')
..writeln(
'### ${i + 1}. [${ranked[i].severity.name}/${ranked[i].confidence}] '
'${ranked[i].rule} — ${ranked[i].location}',
)
..writeln()
..writeln(ranked[i].message);
if (ranked[i].evidence.isNotEmpty) {
@ -580,6 +587,35 @@ Guidance buildGuidance({
);
}
/// Metadata for a report persisted to yams: what produced it, from which
/// project, when, and - when the project is a git checkout - at which commit,
/// so a report read back from the corpus can be dated and reproduced.
String yamsMetadata({
required String project,
required DateTime generated,
String? commit,
}) => [
'task=pipe-guide',
'phase=report',
'source=analysis',
'project=$project',
'generated=${generated.toUtc().toIso8601String()}',
if (commit != null) 'commit=$commit',
].join(',');
/// The HEAD commit of the git checkout at [root], or null when there is none
/// (not a repository, or git is not installed).
Future<String?> gitHead(String root) async {
try {
final result = await Process.run('git', ['-C', root, 'rev-parse', 'HEAD']);
if (result.exitCode != 0) return null;
final head = (result.stdout as String).trim();
return RegExp(r'^[0-9a-f]{7,64}$').hasMatch(head) ? head : null;
} on ProcessException {
return null;
}
}
/// Locate the `yams` CLI on PATH, if present.
String? findYams() {
final path = Platform.environment['PATH'];

View file

@ -143,6 +143,38 @@ void main() {
}
});
test('the persisted markdown carries its caution and each ranking', () {
// --yams persists the markdown, not the brief, and it used to drop both
// the caution line and the [severity/confidence] tag of every finding.
final md = guidance().markdown;
final lines = md.split('\n');
expect(lines[2], startsWith('Static reachability claims'));
final top = rankFindings(analysis.report.newFindings).first;
expect(
md,
contains(
'### 1. [${top.severity.name}/${top.confidence}] '
'${top.rule} — ${top.location}',
),
);
});
test('yams metadata records provenance, not a stale owner', () {
final metadata = yamsMetadata(
project: 'app',
generated: DateTime.utc(2026, 9, 22, 12),
commit: 'abc1234',
);
expect(metadata, contains('project=app'));
expect(metadata, contains('generated=2026-09-22T12:00:00.000Z'));
expect(metadata, contains('commit=abc1234'));
expect(metadata, isNot(contains('owner=')));
expect(
yamsMetadata(project: 'app', generated: DateTime.utc(2026)),
isNot(contains('commit=')),
);
});
test('json summary exposes counts and suppression notes', () {
final summary = guidance().summary;
expect(summary['findings'], analysis.report.findings.length);