feat(api-discovery): add tier=module for module-repo lints
Modules differ from libs structurally: they have no consumable public API (their hooks like M.update are engine-contract, documented in the engine spec, not in the module). The README convention for modules replaces the ## API section with ## Controls + ## Demonstrates. This adds tier=module validation in validate_readme_structure with MUST-sections H1, Abstract via Module-ID badge, Topology + Topology- Block markers, Controls, Demonstrates, References. Order-check ensures Demonstrates precedes References (no API exists to check). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
64
init.lua
64
init.lua
@@ -144,6 +144,10 @@ end
|
|||||||
-- Tier "core" enforces: H1, Abstract, Badges, Topology, Topology-Block, API, References.
|
-- Tier "core" enforces: H1, Abstract, Badges, Topology, Topology-Block, API, References.
|
||||||
-- Tier "engine" enforces the same MUST-sections, but the Badges check accepts
|
-- Tier "engine" enforces the same MUST-sections, but the Badges check accepts
|
||||||
-- any bold key:value line (engine README has no Lib-ID; instead Version/License).
|
-- any bold key:value line (engine README has no Lib-ID; instead Version/License).
|
||||||
|
-- Tier "module" enforces: H1, Abstract via Module-ID badge, Topology + Topology-Block,
|
||||||
|
-- Controls, Demonstrates, References. Modules have no consumable public API
|
||||||
|
-- (engine-hooks are documented in the engine spec), so ## API is replaced by
|
||||||
|
-- ## Controls + ## Demonstrates.
|
||||||
-- Tier "community" enforces nothing (returns empty result).
|
-- Tier "community" enforces nothing (returns empty result).
|
||||||
-- Returns: { missing_sections = [...], section_order_ok = bool }
|
-- Returns: { missing_sections = [...], section_order_ok = bool }
|
||||||
function M.validate_readme_structure(markdown_string, tier)
|
function M.validate_readme_structure(markdown_string, tier)
|
||||||
@@ -151,22 +155,38 @@ function M.validate_readme_structure(markdown_string, tier)
|
|||||||
return { missing_sections = {}, section_order_ok = true }
|
return { missing_sections = {}, section_order_ok = true }
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Both "core" and "engine" tiers use the same MUST-sections list.
|
|
||||||
-- Difference is handled upstream (engine tier skips parse_lua_surface etc.).
|
|
||||||
local missing = {}
|
local missing = {}
|
||||||
local checks = {
|
local checks
|
||||||
{ name = "H1", pattern = "^#%s+%S" },
|
|
||||||
{ name = "Badges", pattern = "\n%*%*Lib%-ID:%*%*%s*lib%-" },
|
|
||||||
{ name = "Topology", pattern = "\n##%s+Topology[%s\n]" },
|
|
||||||
{ name = "Topology-Block", pattern = "<!%-%-%s*topology:start" },
|
|
||||||
{ name = "API", pattern = "\n##%s+API[%s\n]" },
|
|
||||||
{ name = "References", pattern = "\n##%s+References[%s\n]" },
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Engine-tier: relax the Badges check (engine uses "Version", "License" etc.
|
if tier == "module" then
|
||||||
-- instead of a "Lib-ID:" prefix — engine is not a lib).
|
-- Module-tier: no ## API (engine-hooks aren't a consumable surface);
|
||||||
if tier == "engine" then
|
-- Badges identifies the module via **Module-ID:**; Controls + Demonstrates
|
||||||
checks[2] = { name = "Badges", pattern = "\n%*%*[%w%-]+:%*%*" }
|
-- are the module-specific README sections.
|
||||||
|
checks = {
|
||||||
|
{ name = "H1", pattern = "^#%s+%S" },
|
||||||
|
{ name = "Badges", pattern = "\n%*%*Module%-ID:%*%*%s*" },
|
||||||
|
{ name = "Topology", pattern = "\n##%s+Topology[%s\n]" },
|
||||||
|
{ name = "Topology-Block", pattern = "<!%-%-%s*topology:start" },
|
||||||
|
{ name = "Controls", pattern = "\n##%s+Controls[%s\n]" },
|
||||||
|
{ name = "Demonstrates", pattern = "\n##%s+Demonstrates[%s\n]" },
|
||||||
|
{ name = "References", pattern = "\n##%s+References[%s\n]" },
|
||||||
|
}
|
||||||
|
else
|
||||||
|
-- Both "core" and "engine" tiers use the same MUST-sections list.
|
||||||
|
-- Difference is handled upstream (engine tier skips parse_lua_surface etc.).
|
||||||
|
checks = {
|
||||||
|
{ name = "H1", pattern = "^#%s+%S" },
|
||||||
|
{ name = "Badges", pattern = "\n%*%*Lib%-ID:%*%*%s*lib%-" },
|
||||||
|
{ name = "Topology", pattern = "\n##%s+Topology[%s\n]" },
|
||||||
|
{ name = "Topology-Block", pattern = "<!%-%-%s*topology:start" },
|
||||||
|
{ name = "API", pattern = "\n##%s+API[%s\n]" },
|
||||||
|
{ name = "References", pattern = "\n##%s+References[%s\n]" },
|
||||||
|
}
|
||||||
|
-- Engine-tier: relax the Badges check (engine uses "Version", "License" etc.
|
||||||
|
-- instead of a "Lib-ID:" prefix — engine is not a lib).
|
||||||
|
if tier == "engine" then
|
||||||
|
checks[2] = { name = "Badges", pattern = "\n%*%*[%w%-]+:%*%*" }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, c in ipairs(checks) do
|
for _, c in ipairs(checks) do
|
||||||
@@ -175,10 +195,18 @@ function M.validate_readme_structure(markdown_string, tier)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Order check: API must appear before References in source order
|
-- Order check: for module tier Demonstrates must precede References
|
||||||
local api_pos = string.find(markdown_string, "\n##%s+API[%s\n]")
|
-- (no API exists to check); for non-module tiers API must precede References.
|
||||||
local ref_pos = string.find(markdown_string, "\n##%s+References[%s\n]")
|
local order_ok
|
||||||
local order_ok = (api_pos and ref_pos and api_pos < ref_pos) or (not api_pos and not ref_pos)
|
if tier == "module" then
|
||||||
|
local dem_pos = string.find(markdown_string, "\n##%s+Demonstrates[%s\n]")
|
||||||
|
local ref_pos = string.find(markdown_string, "\n##%s+References[%s\n]")
|
||||||
|
order_ok = (dem_pos and ref_pos and dem_pos < ref_pos) or (not dem_pos and not ref_pos)
|
||||||
|
else
|
||||||
|
local api_pos = string.find(markdown_string, "\n##%s+API[%s\n]")
|
||||||
|
local ref_pos = string.find(markdown_string, "\n##%s+References[%s\n]")
|
||||||
|
order_ok = (api_pos and ref_pos and api_pos < ref_pos) or (not api_pos and not ref_pos)
|
||||||
|
end
|
||||||
|
|
||||||
return { missing_sections = missing, section_order_ok = order_ok }
|
return { missing_sections = missing, section_order_ok = order_ok }
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user