kicad-validity B3+C+S: revert toast, corpus lint, .kicad_mod tier

B3 (editor): kicad-binding observes kdoc_meta.revertNonce (deduped, like
seedNonce) → DOC_REVERTED_EVENT → WasmTool doc-reverted-toast (15 s,
data-testid for the deferred e2e check).
C: tests/tools/corpus-lint.ts (npm run corpus:lint) — kicad_tools --lint
over the fixture corpora raw + shared-codec round-trips
(docToFile(fileToDoc(x))); the E3/wrapInBoardEnvelope-class gate. Skips
when output/kicad_tools.js isn't built; CI step lands when kicad_tools
joins the CI app set. 7 fixtures / 7 round-trips green.
S: --lint + --resave gain the .kicad_mod tier (pcbToolsLintFootprint /
pcbToolsResaveFootprint; writer needs CTL_FOR_LIBRARY or the board flags
strip the (version) header). Verified: qa footprint round-trip lints
clean, resave stamps 20260206, garbage exits 1. Also bumps pcbjam-shared
(mergeYUpdates for the bisect pass).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HLua64PCVwkQ1hpWdaf1Gm
This commit is contained in:
Gergő Törcsvári 2026-07-14 10:57:46 +02:00
commit fb2aa2759d
No known key found for this signature in database
GPG key ID: 8E75F2CDE64E5322
8 changed files with 323 additions and 7 deletions

View file

@ -69,8 +69,11 @@
#include <layer_ids.h>
#include <lset.h>
#include <libraries/library_manager.h>
#include <footprint.h>
#include <pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.h>
#include <pcb_io/pcb_io_mgr.h>
#include <pcb_marker.h>
#include <richio.h>
#include <pcb_plotter.h>
#include <pcbplot.h>
#include <pgm_base.h>
@ -681,6 +684,120 @@ int pcbToolsLintBoard( const char* aInPath, std::string& aError )
}
// Full-parse footprint lint for the merged image's --lint driver
// (kicad-validity 0001 S): parse one .kicad_mod via the s-expr plugin's
// ImportFootprint. Returns the pad count, or -1 with aError filled.
int pcbToolsLintFootprint( const char* aInPath, std::string& aError )
{
wxFileName fn( wxString::FromUTF8( aInPath ) );
fn.MakeAbsolute();
kiRuntime();
try
{
PCB_IO_KICAD_SEXPR plugin;
wxString name;
std::unique_ptr<FOOTPRINT> fp( plugin.ImportFootprint( fn.GetFullPath(), name ) );
if( !fp )
{
aError = std::string( aInPath ) + ": error: not a footprint file";
return -1;
}
return (int) fp->Pads().size();
}
catch( PARSE_ERROR& pe )
{
char buf[1024];
std::snprintf( buf, sizeof( buf ), "%s:%d:%d: error: %s", aInPath, pe.lineNumber,
pe.byteIndex, (const char*) pe.ParseProblem().ToUTF8() );
aError = buf;
return -1;
}
catch( const IO_ERROR& ioe )
{
aError = std::string( aInPath ) + ": error: " + std::string( ioe.Problem().ToUTF8() );
return -1;
}
catch( const std::exception& e )
{
aError = std::string( aInPath ) + ": error: " + e.what();
return -1;
}
}
// Footprint resave for --resave (kicad-validity 0001 S): parse + rewrite one
// .kicad_mod in the current format, the exact FP_CACHE::Save incantation
// (PRETTIFIED formatter + plugin Format). 0 / 4 load failed / 5 write failed.
int pcbToolsResaveFootprint( const char* aInPath, const char* aOutPath, std::string& aError )
{
wxFileName fn( wxString::FromUTF8( aInPath ) );
fn.MakeAbsolute();
kiRuntime();
std::unique_ptr<FOOTPRINT> fp;
try
{
PCB_IO_KICAD_SEXPR plugin;
wxString name;
fp.reset( plugin.ImportFootprint( fn.GetFullPath(), name ) );
if( !fp )
{
aError = std::string( aInPath ) + ": error: not a footprint file";
return 4;
}
}
catch( PARSE_ERROR& pe )
{
char buf[1024];
std::snprintf( buf, sizeof( buf ), "%s:%d:%d: error: %s", aInPath, pe.lineNumber,
pe.byteIndex, (const char*) pe.ParseProblem().ToUTF8() );
aError = buf;
return 4;
}
catch( const IO_ERROR& ioe )
{
aError = std::string( aInPath ) + ": error: " + std::string( ioe.Problem().ToUTF8() );
return 4;
}
catch( const std::exception& e )
{
aError = std::string( aInPath ) + ": error: " + e.what();
return 4;
}
try
{
// CTL_FOR_LIBRARY, NOT the default CTL_FOR_BOARD — the board flags
// include CTL_OMIT_FOOTPRINT_VERSION, which would strip the (version)
// header a standalone .kicad_mod must carry (FootprintSave sets the
// same flags before formatting).
PCB_IO_KICAD_SEXPR writer( CTL_FOR_LIBRARY );
PRETTIFIED_FILE_OUTPUTFORMATTER formatter( wxString::FromUTF8( aOutPath ) );
writer.SetOutputFormatter( &formatter );
writer.Format( fp.get() );
}
catch( const IO_ERROR& ioe )
{
aError = std::string( aOutPath ) + ": error: " + std::string( ioe.Problem().ToUTF8() );
return 5;
}
catch( const std::exception& e )
{
aError = std::string( aOutPath ) + ": error: " + e.what();
return 5;
}
return 0;
}
// Full-parse board resave for the merged image's --resave driver (also on the
// eeschema side): a bare load like pcbToolsLintBoard, then the s-expr writer
// rewrites the board in the current format version. Zones are written