@@ -98,6 +98,20 @@ func TestCheckLinks_HTTP(t *testing.T) {
9898 mux .HandleFunc ("/not-found" , func (w http.ResponseWriter , r * http.Request ) {
9999 w .WriteHeader (http .StatusNotFound )
100100 })
101+ mux .HandleFunc ("/head-404-get-200" , func (w http.ResponseWriter , r * http.Request ) {
102+ if r .Method == http .MethodHead {
103+ w .WriteHeader (http .StatusNotFound )
104+ return
105+ }
106+ w .WriteHeader (http .StatusOK )
107+ })
108+ mux .HandleFunc ("/head-405-get-200" , func (w http.ResponseWriter , r * http.Request ) {
109+ if r .Method == http .MethodHead {
110+ w .WriteHeader (http .StatusMethodNotAllowed )
111+ return
112+ }
113+ w .WriteHeader (http .StatusOK )
114+ })
101115 mux .HandleFunc ("/forbidden" , func (w http.ResponseWriter , r * http.Request ) {
102116 w .WriteHeader (http .StatusForbidden )
103117 })
@@ -135,6 +149,20 @@ func TestCheckLinks_HTTP(t *testing.T) {
135149 requireResultContaining (t , results , types .Error , "HTTP 500" )
136150 })
137151
152+ t .Run ("HEAD 404 falls back to GET 200" , func (t * testing.T ) {
153+ dir := t .TempDir ()
154+ body := "[spa](" + server .URL + "/head-404-get-200)"
155+ results := CheckLinks (t .Context (), dir , body )
156+ requireResultContaining (t , results , types .Pass , "HTTP 200" )
157+ })
158+
159+ t .Run ("HEAD 405 falls back to GET 200" , func (t * testing.T ) {
160+ dir := t .TempDir ()
161+ body := "[nohead](" + server .URL + "/head-405-get-200)"
162+ results := CheckLinks (t .Context (), dir , body )
163+ requireResultContaining (t , results , types .Pass , "HTTP 200" )
164+ })
165+
138166 t .Run ("mixed relative and HTTP only checks HTTP" , func (t * testing.T ) {
139167 dir := t .TempDir ()
140168 writeFile (t , dir , "references/guide.md" , "content" )
@@ -225,6 +253,70 @@ func TestCheckHTTPLink(t *testing.T) {
225253 requireContains (t , result .Message , "HTTP 403" )
226254 })
227255
256+ t .Run ("HEAD 404 retries with GET" , func (t * testing.T ) {
257+ server := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
258+ if r .Method == http .MethodHead {
259+ w .WriteHeader (http .StatusNotFound )
260+ return
261+ }
262+ w .WriteHeader (http .StatusOK )
263+ }))
264+ defer server .Close ()
265+
266+ result := checkHTTPLink (types.ResultContext {Category : "Links" , File : "SKILL.md" }, client , server .URL )
267+ if result .Level != types .Pass {
268+ t .Errorf ("expected Pass after GET fallback, got level=%d message=%q" , result .Level , result .Message )
269+ }
270+ })
271+
272+ t .Run ("HEAD 405 retries with GET" , func (t * testing.T ) {
273+ server := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
274+ if r .Method == http .MethodHead {
275+ w .WriteHeader (http .StatusMethodNotAllowed )
276+ return
277+ }
278+ w .WriteHeader (http .StatusOK )
279+ }))
280+ defer server .Close ()
281+
282+ result := checkHTTPLink (types.ResultContext {Category : "Links" , File : "SKILL.md" }, client , server .URL )
283+ if result .Level != types .Pass {
284+ t .Errorf ("expected Pass after GET fallback, got level=%d message=%q" , result .Level , result .Message )
285+ }
286+ })
287+
288+ t .Run ("SPA requiring Accept text/html resolves via GET fallback" , func (t * testing.T ) {
289+ server := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
290+ if r .Header .Get ("Accept" ) == "" || r .Method == http .MethodHead {
291+ w .WriteHeader (http .StatusNotFound )
292+ return
293+ }
294+ if strings .Contains (r .Header .Get ("Accept" ), "text/html" ) {
295+ w .WriteHeader (http .StatusOK )
296+ return
297+ }
298+ w .WriteHeader (http .StatusNotFound )
299+ }))
300+ defer server .Close ()
301+
302+ result := checkHTTPLink (types.ResultContext {Category : "Links" , File : "SKILL.md" }, client , server .URL )
303+ if result .Level != types .Pass {
304+ t .Errorf ("expected Pass for SPA with Accept header, got level=%d message=%q" , result .Level , result .Message )
305+ }
306+ })
307+
308+ t .Run ("genuine 404 still errors after GET fallback" , func (t * testing.T ) {
309+ server := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
310+ w .WriteHeader (http .StatusNotFound )
311+ }))
312+ defer server .Close ()
313+
314+ result := checkHTTPLink (types.ResultContext {Category : "Links" , File : "SKILL.md" }, client , server .URL )
315+ if result .Level != types .Error {
316+ t .Errorf ("expected Error for genuine 404, got level=%d message=%q" , result .Level , result .Message )
317+ }
318+ })
319+
228320 t .Run ("invalid URL" , func (t * testing.T ) {
229321 result := checkHTTPLink (types.ResultContext {Category : "Links" , File : "SKILL.md" }, client , "http://invalid host with spaces/" )
230322 if result .Level != types .Error {
0 commit comments