Skip to content

Commit e50d8e0

Browse files
authored
feat: audit tracking tooling (#13639)
The beginning of an audit tracking framework. Adds a header of the following form to every relevant file: ``` // === AUDIT STATUS === // internal: { status: not started, auditors: [], date: YYYY-MM-DD } // external_1: { status: not started, auditors: [], date: YYYY-MM-DD } // external_2: { status: not started, auditors: [], date: YYYY-MM-DD } // ===================== ``` These headers will be manually updated as the audit state changes for a given file. These headers are parsed to produce an html dashboard visualizing the progress of the audit. See below for a screenshot of the dashboard. We can eventually iterate on this to include automatic regression tracking etc. Ideally the dashboard can eventually be automatically updated and hosted similar to our bench page.
1 parent 2b6c627 commit e50d8e0

File tree

673 files changed

+4684
-3
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

673 files changed

+4684
-3
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
AUDIT TRACKING DASHBOARD
2+
=========================
3+
4+
Tooling for tracking the audit status of barretenberg. Audit metadata is embedded directly in each source file and summarized in an interactive dashboard.
5+
6+
HOW IT WORKS
7+
------------
8+
9+
1. Audit headers in source files
10+
Each file being audited includes a structured comment block with audit status for multiple roles, e.g.:
11+
12+
// === AUDIT STATUS ===
13+
// internal: { status: not started, auditors: [], date: YYYY-MM-DD }
14+
// external_1: { status: not started, auditors: [], date: YYYY-MM-DD }
15+
// external_2: { status: not started, auditors: [], date: YYYY-MM-DD }
16+
// =====================
17+
18+
The script generate_audit_status_headers.sh populates all source files (in included dirs) with this header, unless one is already present.
19+
20+
2. The header in each file should be manually updated when the audit status changes, e.g.:
21+
22+
// === AUDIT STATUS ===
23+
// internal: { status: complete, auditors: [luke], date: 2025-04-17 }
24+
// external_1: { status: not started, auditors: [], date: YYYY-MM-DD }
25+
// external_2: { status: not started, auditors: [], date: YYYY-MM-DD }
26+
// =====================
27+
28+
3. Summary generation
29+
A script (generate_audit_summary.py) scans the codebase and produces a `audit_summary.json` file with the status breakdown per module and role.
30+
31+
4. Dashboard visualization
32+
A static HTML file (audit_dashboard.html) visualizes the JSON using pie charts, with one chart per audit role per module. A summary section at the top gives a full-repo overview.
33+
34+
USAGE
35+
-----
36+
37+
1. Run the server on the remote machine:
38+
39+
./scripts/audit/run_dashboard_server.sh
40+
41+
2. Open a tunnel from your local machine:
42+
43+
ssh -L 8080:localhost:8080 youruser@remotehost
44+
45+
3. View the dashboard in your browser:
46+
47+
http://localhost:8080/audit_dashboard.html
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>Barretenberg Audit Dashboard</title>
6+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
7+
<style>
8+
body { font-family: sans-serif; padding: 2rem; }
9+
.legend-container {
10+
margin-bottom: 2rem;
11+
}
12+
.chart-grid {
13+
display: flex;
14+
flex-wrap: wrap;
15+
gap: 2rem;
16+
}
17+
.module-section {
18+
width: calc(50% - 1rem);
19+
border: 1px solid #ccc;
20+
border-radius: 8px;
21+
padding: 1rem;
22+
}
23+
.role-charts {
24+
display: flex;
25+
flex-wrap: wrap;
26+
gap: 1rem;
27+
justify-content: center;
28+
}
29+
canvas {
30+
width: 180px !important;
31+
height: 180px !important;
32+
}
33+
</style>
34+
</head>
35+
<body>
36+
<h1>Barretenberg Audit Dashboard</h1>
37+
<div id="legend" class="legend-container"></div>
38+
<div id="charts" class="chart-grid"></div>
39+
40+
<script>
41+
fetch('audit_summary.json')
42+
.then(res => res.json())
43+
.then(auditSummary => {
44+
const chartsContainer = document.getElementById("charts");
45+
const legendContainer = document.getElementById("legend");
46+
47+
const roleColors = {
48+
"not started": "#bdc3c7",
49+
"in progress": "#f1c40f",
50+
"complete": "#2ecc71",
51+
"unknown": "#e74c3c"
52+
};
53+
54+
// Create shared legend
55+
const legendItems = Object.entries(roleColors).map(([status, color]) => {
56+
return `<span style="display: inline-block; margin-right: 1rem;">
57+
<span style="display:inline-block; width: 12px; height: 12px; background:${color}; margin-right: 5px; border-radius: 2px;"></span>
58+
${status}
59+
</span>`;
60+
}).join('');
61+
legendContainer.innerHTML = `<strong>Legend:</strong><br>${legendItems}`;
62+
63+
// Summary chart data by role
64+
const summaryByRole = {};
65+
const moduleFileCounts = {};
66+
67+
for (const [module, roles] of Object.entries(auditSummary)) {
68+
const uniqueFiles = new Set();
69+
const moduleDiv = document.createElement("div");
70+
moduleDiv.className = "module-section";
71+
72+
const roleChartRow = document.createElement("div");
73+
roleChartRow.className = "role-charts";
74+
75+
for (const [role, statuses] of Object.entries(roles)) {
76+
const canvas = document.createElement("canvas");
77+
const chartId = `${module}-${role}`;
78+
canvas.id = chartId;
79+
roleChartRow.appendChild(canvas);
80+
81+
const labels = Object.keys(statuses);
82+
const data = Object.values(statuses);
83+
84+
labels.forEach((label, i) => {
85+
summaryByRole[role] = summaryByRole[role] || {};
86+
summaryByRole[role][label] = (summaryByRole[role][label] || 0) + data[i];
87+
});
88+
89+
const backgroundColors = labels.map(label => roleColors[label] || "#95a5a6");
90+
91+
new Chart(canvas.getContext("2d"), {
92+
type: "pie",
93+
data: {
94+
labels: labels,
95+
datasets: [{
96+
data: data,
97+
backgroundColor: backgroundColors
98+
}]
99+
},
100+
options: {
101+
responsive: true,
102+
plugins: {
103+
legend: {
104+
display: false
105+
},
106+
title: {
107+
display: true,
108+
text: role
109+
}
110+
}
111+
}
112+
});
113+
}
114+
115+
const fileCount = Object.values(roles)[0] ? Object.values(Object.values(roles)[0]).reduce((a, b) => a + b, 0) : 0;
116+
moduleFileCounts[module] = fileCount;
117+
118+
moduleDiv.innerHTML = `<h3>${module} (${fileCount} files)</h3>`;
119+
moduleDiv.appendChild(roleChartRow);
120+
chartsContainer.appendChild(moduleDiv);
121+
}
122+
123+
// Create summary module
124+
const totalFiles = Object.values(moduleFileCounts).reduce((a, b) => a + b, 0);
125+
const summaryDiv = document.createElement("div");
126+
summaryDiv.className = "module-section";
127+
summaryDiv.innerHTML = `<h3>Summary (${totalFiles} files)</h3>`;
128+
const summaryChartRow = document.createElement("div");
129+
summaryChartRow.className = "role-charts";
130+
131+
for (const [role, statuses] of Object.entries(summaryByRole)) {
132+
const canvas = document.createElement("canvas");
133+
summaryChartRow.appendChild(canvas);
134+
135+
const labels = Object.keys(statuses);
136+
const data = Object.values(statuses);
137+
const colors = labels.map(label => roleColors[label] || "#95a5a6");
138+
139+
new Chart(canvas.getContext("2d"), {
140+
type: "pie",
141+
data: {
142+
labels: labels,
143+
datasets: [{
144+
data: data,
145+
backgroundColor: colors
146+
}]
147+
},
148+
options: {
149+
responsive: true,
150+
plugins: {
151+
legend: {
152+
display: false
153+
},
154+
title: {
155+
display: true,
156+
text: role
157+
}
158+
}
159+
}
160+
});
161+
}
162+
163+
summaryDiv.appendChild(summaryChartRow);
164+
chartsContainer.prepend(summaryDiv);
165+
})
166+
.catch(err => {
167+
document.body.innerHTML += `<p style=\"color:red\">Failed to load audit_summary.json</p>`;
168+
console.error(err);
169+
});
170+
</script>
171+
</body>
172+
</html>

0 commit comments

Comments
 (0)