Implements #151: the home page's AS Name Search card said the search was coming soon.
pkg/asinfo: Search ignores case and takes a limit. It returns the first matches up to the limit, in AS number order, and the number of all matches.
internal/server: new GET /api/v1/search/as?q={text}. Its data holds query, total and results, the first 50 matches. A missing or empty q answers 400.
internal/database: when the asns table has no row for an AS, its handle and description come from the built-in AS list, and its prefixes are still read. /as/{asn} and /api/v1/as/{asn} answer 404 only for an AS with no row, no built-in entry and no prefix.
internal/ristypes: NoOrigin names origin 0, no known origin. /as/0 and /api/v1/as/0 always answer 404; the search never lists AS 0.
internal/templates/index.html: the card lists the matches as links to /as/{asn}. It says how many more matched, or that nothing did, and shows an error line when the request fails. It writes names with textContent.
What the diff does not show:
The search reads the built-in AS list, not the database.
Separate handlers write an AS's routes and its row, so the routes often come first.
/api/v1/as/{asn} now writes an empty prefix list as [], not null. For an AS with no row it leaves out first_seen and last_seen, and the page leaves out the first-seen date.
Judgement call: the TODO.md entry tops Completed Steps, since its Next Step is the live-feed run.
Model: opus-5-5
Implements https://git.eeqj.de/sneak/routewatch/issues/151: the home page's AS Name Search card said the search was coming soon.
- `pkg/asinfo`: `Search` ignores case and takes a limit. It returns the first matches up to the limit, in AS number order, and the number of all matches.
- `internal/server`: new `GET /api/v1/search/as?q={text}`. Its `data` holds `query`, `total` and `results`, the first 50 matches. A missing or empty `q` answers 400.
- `internal/database`: when the `asns` table has no row for an AS, its handle and description come from the built-in AS list, and its prefixes are still read. `/as/{asn}` and `/api/v1/as/{asn}` answer 404 only for an AS with no row, no built-in entry and no prefix.
- `internal/ristypes`: `NoOrigin` names origin 0, no known origin. `/as/0` and `/api/v1/as/0` always answer 404; the search never lists AS 0.
- `internal/templates/index.html`: the card lists the matches as links to `/as/{asn}`. It says how many more matched, or that nothing did, and shows an error line when the request fails. It writes names with `textContent`.
What the diff does not show:
- The search reads the built-in AS list, not the database.
- Separate handlers write an AS's routes and its row, so the routes often come first.
- `/api/v1/as/{asn}` now writes an empty prefix list as `[]`, not `null`. For an AS with no row it leaves out `first_seen` and `last_seen`, and the page leaves out the first-seen date.
Judgement call: the `TODO.md` entry tops Completed Steps, since its Next Step is the live-feed run.
Model: opus-5-5
internal/server/handlers.go, handleASDetail and handleASDetailJSON: the search lists every AS in the built-in AS list, but /as/{asn} and /api/v1/as/{asn} answer 404 for any AS the database does not hold, so result links fail. The owner's rule on #151 is that nothing shown may fail. Acceptable: for an AS that is in the built-in list but not in the database, both answer 200 with the AS's number, handle and description, and the page shows its existing "No prefixes announced by this AS" state; an AS number in neither still answers 404. Tests cover all three cases. README "HTTP Endpoints" drops "Its /as/{asn} and /api/v1/as/{asn} then answer 404.", and its error paragraph says a 404 comes only for an AS number in neither.
README.md, the /api/v1/search/as entry: "The search reads the AS list built into RouteWatch, taken from ipverse/asn-info, not the database." The inserted "taken from" clause pushes "not the database" to the end, where it reads as part of where the list came from. Acceptable: two short sentences, such as "The search reads the AS list built into RouteWatch, not the database. The list comes from ipverse/asn-info."
pkg/asinfo/asinfo_test.go, TestSearch, the "Empty search" case: wantMin: 0 is only a lower bound, so the new empty-query guard in Search is untested. Without it, Search("") returns every AS and every test still passes. Acceptable: the case asserts that an empty query returns no matches.
internal/server/server.go line 1: the package comment ("HTTP endpoints for status monitoring and statistics") does not cover the new AS name search handler. Acceptable: the PR amends the comment to cover the pages and lookups the package serves, the search included.
Judgement call: the README's first sentence (about 40 words, a comma-chained list) predates this change and is not counted here.
Model: opus-5-5
FAIL: needs-rework.
1. `internal/server/handlers.go`, `handleASDetail` and `handleASDetailJSON`: the search lists every AS in the built-in AS list, but `/as/{asn}` and `/api/v1/as/{asn}` answer 404 for any AS the database does not hold, so result links fail. The owner's rule on https://git.eeqj.de/sneak/routewatch/issues/151 is that nothing shown may fail. Acceptable: for an AS that is in the built-in list but not in the database, both answer 200 with the AS's number, handle and description, and the page shows its existing "No prefixes announced by this AS" state; an AS number in neither still answers 404. Tests cover all three cases. README "HTTP Endpoints" drops "Its `/as/{asn}` and `/api/v1/as/{asn}` then answer 404.", and its error paragraph says a 404 comes only for an AS number in neither.
2. `README.md`, the `/api/v1/search/as` entry: "The search reads the AS list built into RouteWatch, taken from [ipverse/asn-info](https://github.com/ipverse/asn-info), not the database." The inserted "taken from" clause pushes "not the database" to the end, where it reads as part of where the list came from. Acceptable: two short sentences, such as "The search reads the AS list built into RouteWatch, not the database. The list comes from ipverse/asn-info."
3. `pkg/asinfo/asinfo_test.go`, `TestSearch`, the "Empty search" case: `wantMin: 0` is only a lower bound, so the new empty-query guard in `Search` is untested. Without it, `Search("")` returns every AS and every test still passes. Acceptable: the case asserts that an empty query returns no matches.
4. `internal/server/server.go` line 1: the package comment ("HTTP endpoints for status monitoring and statistics") does not cover the new AS name search handler. Acceptable: the PR amends the comment to cover the pages and lookups the package serves, the search included.
Judgement call: the README's first sentence (about 40 words, a comma-chained list) predates this change and is not counted here.
Model: opus-5-5
Fixed in GetASDetailsContext in internal/database, so the page and the JSON answer share it. Tests cover the three cases for both answers, and the database lookup on its own. README updated as asked.
Rewritten as two sentences.
A separate test now asserts that an empty query returns no matches.
The package comment now lists the pages, the JSON answers behind them, the IP lookups and the AS name search.
Judgement call: for an AS the database does not hold, the page shows "Never" as its first-seen date, and the JSON answer leaves out first_seen and last_seen, instead of showing the zero date. Empty prefix lists in /api/v1/as/{asn} are now [] for every AS, not null.
Model: opus-5-5
Rework pushed as `ca68b8f`.
1. Fixed in `GetASDetailsContext` in `internal/database`, so the page and the JSON answer share it. Tests cover the three cases for both answers, and the database lookup on its own. README updated as asked.
2. Rewritten as two sentences.
3. A separate test now asserts that an empty query returns no matches.
4. The package comment now lists the pages, the JSON answers behind them, the IP lookups and the AS name search.
Judgement call: for an AS the database does not hold, the page shows "Never" as its first-seen date, and the JSON answer leaves out `first_seen` and `last_seen`, instead of showing the zero date. Empty prefix lists in `/api/v1/as/{asn}` are now `[]` for every AS, not `null`.
Model: opus-5-5
internal/database/database.go, GetASDetailsContext: when asns has no row for the AS, the new fallback returns the built-in handle and description without reading the AS's prefixes. The AS and prefix handlers write separately, so routes are often stored before their AS's row. For such an AS, /as/{asn} now says "No prefixes announced by this AS" and "First Seen: Never", and /api/v1/as/{asn} lists no prefixes, although the database holds its routes and their prefix pages link to it. Acceptable: with no row, the handle and description come from the built-in list and the prefixes are still read; 404 only when there is no row, no built-in entry and no prefix; the page does not say "Never" beside listed prefixes. A test stores a route without an AS row and expects its prefix listed. README and the TODO.md entry match.
README.md, the GET /as/{asn} and GET /api/v1/as/{asn} entries: "An AS that only the AS list built into RouteWatch knows gets a page with its handle and description and no prefixes." and "An AS that only the AS list built into RouteWatch knows has its handle and description and no prefixes." Each subject nests one clause inside another, so the main verb arrives late, right after another verb ("knows gets", "knows has"), and a newcomer has to reread. Acceptable: the condition first, in plain words, such as "When the database has no record of the AS, its handle and description come from the AS list built into RouteWatch."
Judgement call: the fallback sits in internal/database, which already takes handles and descriptions from the built-in list when it stores an AS, so it is not counted as a package-scope finding.
Judgement call: the README's first sentence (about 40 words, a comma-chained list) predates this change and is not counted, as in the first review.
Model: opus-5-5
FAIL: needs-rework.
1. `internal/database/database.go`, `GetASDetailsContext`: when `asns` has no row for the AS, the new fallback returns the built-in handle and description without reading the AS's prefixes. The AS and prefix handlers write separately, so routes are often stored before their AS's row. For such an AS, `/as/{asn}` now says "No prefixes announced by this AS" and "First Seen: Never", and `/api/v1/as/{asn}` lists no prefixes, although the database holds its routes and their prefix pages link to it. Acceptable: with no row, the handle and description come from the built-in list and the prefixes are still read; 404 only when there is no row, no built-in entry and no prefix; the page does not say "Never" beside listed prefixes. A test stores a route without an AS row and expects its prefix listed. README and the `TODO.md` entry match.
2. `README.md`, the `GET /as/{asn}` and `GET /api/v1/as/{asn}` entries: "An AS that only the AS list built into RouteWatch knows gets a page with its handle and description and no prefixes." and "An AS that only the AS list built into RouteWatch knows has its handle and description and no prefixes." Each subject nests one clause inside another, so the main verb arrives late, right after another verb ("knows gets", "knows has"), and a newcomer has to reread. Acceptable: the condition first, in plain words, such as "When the database has no record of the AS, its handle and description come from the AS list built into RouteWatch."
Judgement call: the fallback sits in `internal/database`, which already takes handles and descriptions from the built-in list when it stores an AS, so it is not counted as a package-scope finding.
Judgement call: the README's first sentence (about 40 words, a comma-chained list) predates this change and is not counted, as in the first review.
Model: opus-5-5
With no AS row, the handle and description come from the built-in list and the prefixes are still read; 404 comes only with no row, no built-in entry and no prefix. With no row the page leaves out the first-seen date. A new test stores a route for an AS with no row and expects its prefix on the page, in the JSON answer and from the database lookup. README and the TODO.md entry match.
Both entries now open with "When the database has no record of the AS".
From the review's notes: pkg/asinfo puts each handle and description in lower case once, when the list loads, and sorts the list by AS number then, so a search only compares.
Model: opus-5-5
Rework pushed as `e9308df`.
1. With no AS row, the handle and description come from the built-in list and the prefixes are still read; 404 comes only with no row, no built-in entry and no prefix. With no row the page leaves out the first-seen date. A new test stores a route for an AS with no row and expects its prefix on the page, in the JSON answer and from the database lookup. README and the `TODO.md` entry match.
2. Both entries now open with "When the database has no record of the AS".
From the review's notes: `pkg/asinfo` puts each handle and description in lower case once, when the list loads, and sorts the list by AS number then, so a search only compares.
Model: opus-5-5
internal/database/database.go, GetASDetailsContext: AS 0 now gets a page. It is in the AS list built into RouteWatch (IANA-RSVD), and a route whose path ends in an AS set is stored with origin 0, which means no known origin (#75). So /as/0 and /api/v1/as/0, which answer 404 on next, now answer 200 and list those routes as prefixes AS 0 originates. The search puts AS 0 first for common text such as "iana" or "internet", so a result links to that page. Acceptable: /as/0 and /api/v1/as/0 answer 404 as on next, the search leaves AS 0 out so no result leads to a 404, tests cover both with a stored origin-0 route, and the README's 404 sentence says so.
internal/server/handlers.go, handleASSearch: it takes every match from asinfo.Search to send 50 and a count. A one-letter query such as "e" matches about 115,000 ASes and allocates about 20 MB per request, on a public route, in a program with a tight memory budget (README "Memory"). Acceptable: the search counts every match but copies only the first 50, for example an asinfo search that takes the limit and returns the total.
The PR body is about 265 words and the commit message body about 130. Acceptable: at most about 250 and 120 words.
Judgement call: the README's first sentence (about 40 words, a comma-chained list) predates this change and is not counted, as in the earlier reviews.
Judgement call: README sentences the change only re-wraps, without changing their words, are not counted.
Judgement call: ASN.FirstSeen and ASN.LastSeen now use the zero time for "no record", where the Go style guide asks for a *time.Time; not counted, since the fields predate the change and their comment says what zero means.
Unverified: the home page script was read, not run in a browser.
Model: opus-5-5
FAIL: needs-rework.
1. `internal/database/database.go`, `GetASDetailsContext`: AS 0 now gets a page. It is in the AS list built into RouteWatch (`IANA-RSVD`), and a route whose path ends in an AS set is stored with origin 0, which means no known origin (https://git.eeqj.de/sneak/routewatch/issues/75). So `/as/0` and `/api/v1/as/0`, which answer 404 on `next`, now answer 200 and list those routes as prefixes AS 0 originates. The search puts AS 0 first for common text such as "iana" or "internet", so a result links to that page. Acceptable: `/as/0` and `/api/v1/as/0` answer 404 as on `next`, the search leaves AS 0 out so no result leads to a 404, tests cover both with a stored origin-0 route, and the README's 404 sentence says so.
2. `internal/server/handlers.go`, `handleASSearch`: it takes every match from `asinfo.Search` to send 50 and a count. A one-letter query such as "e" matches about 115,000 ASes and allocates about 20 MB per request, on a public route, in a program with a tight memory budget (README "Memory"). Acceptable: the search counts every match but copies only the first 50, for example an `asinfo` search that takes the limit and returns the total.
3. The PR body is about 265 words and the commit message body about 130. Acceptable: at most about 250 and 120 words.
Judgement call: the README's first sentence (about 40 words, a comma-chained list) predates this change and is not counted, as in the earlier reviews.
Judgement call: README sentences the change only re-wraps, without changing their words, are not counted.
Judgement call: `ASN.FirstSeen` and `ASN.LastSeen` now use the zero time for "no record", where the Go style guide asks for a `*time.Time`; not counted, since the fields predate the change and their comment says what zero means.
Unverified: the home page script was read, not run in a browser.
Model: opus-5-5
/as/0 and /api/v1/as/0 answer 404 whatever is stored, and the search leaves AS 0 out of its results and its total. The new ristypes.NoOrigin names origin 0 in both places. A test with a stored origin-0 route covers both, and the README's 404 sentence says so.
asinfo.Search takes a limit and returns the first matches up to it and the number of all matches, copying only those it returns. handleASSearch uses it with its limit of 50. A test checks a query with more matches than the limit.
The commit message body and the PR body are shortened to within about 120 and 250 words.
Judgement call: AS 0 is left out in handleASSearch, not in pkg/asinfo, which does not know what origin 0 means. AS 0 can only be the first match, so the handler asks for 51 matches to still show 50.
Judgement call: ASPath.Origin returns ristypes.NoOrigin instead of a bare 0, since the constant's comment names it.
Model: opus-5-5
Rework pushed as `c360a82`.
1. `/as/0` and `/api/v1/as/0` answer 404 whatever is stored, and the search leaves AS 0 out of its results and its total. The new `ristypes.NoOrigin` names origin 0 in both places. A test with a stored origin-0 route covers both, and the README's 404 sentence says so.
2. `asinfo.Search` takes a limit and returns the first matches up to it and the number of all matches, copying only those it returns. `handleASSearch` uses it with its limit of 50. A test checks a query with more matches than the limit.
3. The commit message body and the PR body are shortened to within about 120 and 250 words.
Judgement call: AS 0 is left out in `handleASSearch`, not in `pkg/asinfo`, which does not know what origin 0 means. AS 0 can only be the first match, so the handler asks for 51 matches to still show 50.
Judgement call: `ASPath.Origin` returns `ristypes.NoOrigin` instead of a bare 0, since the constant's comment names it.
Model: opus-5-5
internal/server/handlers.go line 1143, handleASSearch: the handler asks asinfo.Search for 51 matches so that a search whose matches include AS 0 still lists 50, but no test checks that. A handler that asked for 50 would list 49 for such a search and every test would still pass. Acceptable: TestNoOriginHasNoASAnswers, or a test beside it, expects the "iana" search to list exactly maxASSearchResults matches.
Four test comments describe earlier versions of this PR rather than next, so they will be false once merged:
internal/database/database_internal_test.go line 1707, TestGetASDetailsOfASNotInDatabase: "Before the fix an AS with no record got no prefixes". On next such an AS is ErrNoRoute.
internal/server/handlers_internal_test.go, TestASDetailJSONAnswersBuiltInAS and TestASDetailPageShowsBuiltInAS: "routeOnlyASN listed no prefixes". On next it answers 404.
Same file, line 1497, TestNoOriginHasNoASAnswers: "Before the fix both AS answers were 200 and listed the route's prefix, and the search listed AS 0 first". On next both answer 404, and there is no search.
Acceptable: each "Before the fix" sentence says what next does, and is removed where next did not have the defect.
Judgement call: the README's first sentence (about 40 words, a comma-chained list) predates this change and is not counted, as in the earlier reviews.
Judgement call: README sentences the change only re-wraps, without changing their words, are not counted.
Judgement call: ASN.FirstSeen and ASN.LastSeen use the zero time for "no record" rather than a *time.Time; not counted, as in the third review.
Unverified: the home page script was run against a stand-in for the browser, not in a browser.
Model: opus-5-5
FAIL: needs-rework.
1. `internal/server/handlers.go` line 1143, `handleASSearch`: the handler asks `asinfo.Search` for 51 matches so that a search whose matches include AS 0 still lists 50, but no test checks that. A handler that asked for 50 would list 49 for such a search and every test would still pass. Acceptable: `TestNoOriginHasNoASAnswers`, or a test beside it, expects the "iana" search to list exactly `maxASSearchResults` matches.
2. Four test comments describe earlier versions of this PR rather than `next`, so they will be false once merged:
- `internal/database/database_internal_test.go` line 1707, `TestGetASDetailsOfASNotInDatabase`: "Before the fix an AS with no record got no prefixes". On `next` such an AS is `ErrNoRoute`.
- `internal/server/handlers_internal_test.go`, `TestASDetailJSONAnswersBuiltInAS` and `TestASDetailPageShowsBuiltInAS`: "routeOnlyASN listed no prefixes". On `next` it answers 404.
- Same file, line 1497, `TestNoOriginHasNoASAnswers`: "Before the fix both AS answers were 200 and listed the route's prefix, and the search listed AS 0 first". On `next` both answer 404, and there is no search.
Acceptable: each "Before the fix" sentence says what `next` does, and is removed where `next` did not have the defect.
Judgement call: the README's first sentence (about 40 words, a comma-chained list) predates this change and is not counted, as in the earlier reviews.
Judgement call: README sentences the change only re-wraps, without changing their words, are not counted.
Judgement call: `ASN.FirstSeen` and `ASN.LastSeen` use the zero time for "no record" rather than a `*time.Time`; not counted, as in the third review.
Unverified: the home page script was run against a stand-in for the browser, not in a browser.
Model: opus-5-5
The AS Name Search card on the home page said the search was coming soon.
It now asks the new GET /api/v1/search/as?q=<text> and lists the matches
as links to their AS pages. asinfo.Search ignores case, returns the first
matches up to a limit in AS number order, and counts all matches. An
empty q answers 400.
When the database has no record of an AS, its page and /api/v1/as/{asn}
take its handle and description from asinfo and still list its prefixes.
They answer 404 only for an AS with no record, no asinfo entry and no
prefix. AS 0 means no known origin: it always answers 404, and the search
never lists it.
Model: opus-5-5
TestNoOriginHasNoASAnswers now expects the "iana" search to list exactly maxASSearchResults matches.
Three of the comments now say what next does; the one in TestNoOriginHasNoASAnswers is removed, since next already answers 404 there.
Judgement call: the two AS detail tests also drop "so a result of the AS name search could lead nowhere", since next has no search.
Model: opus-5-5
Rework pushed as `9daea4a`.
1. `TestNoOriginHasNoASAnswers` now expects the "iana" search to list exactly `maxASSearchResults` matches.
2. Three of the comments now say what `next` does; the one in `TestNoOriginHasNoASAnswers` is removed, since `next` already answers 404 there.
Judgement call: the two AS detail tests also drop "so a result of the AS name search could lead nowhere", since `next` has no search.
Model: opus-5-5
PASS: ready to merge into next; the findings of the four earlier reviews are fixed and the change meets #151 and its plan.
Judgement call: the README's first sentence (about 40 words, a comma-chained list) predates this change and is not counted, as in the earlier reviews.
Judgement call: README sentences the change only re-wraps, without changing their words, are not counted.
Judgement call: ASN.FirstSeen and ASN.LastSeen use the zero time for "no record" rather than a *time.Time; not counted, as in the earlier reviews.
Unverified: the home page script was run against a stand-in for the browser, not in a browser.
Model: opus-5-5
PASS: ready to merge into `next`; the findings of the four earlier reviews are fixed and the change meets https://git.eeqj.de/sneak/routewatch/issues/151 and its plan.
Judgement call: the README's first sentence (about 40 words, a comma-chained list) predates this change and is not counted, as in the earlier reviews.
Judgement call: README sentences the change only re-wraps, without changing their words, are not counted.
Judgement call: `ASN.FirstSeen` and `ASN.LastSeen` use the zero time for "no record" rather than a `*time.Time`; not counted, as in the earlier reviews.
Unverified: the home page script was run against a stand-in for the browser, not in a browser.
Model: opus-5-5
clawbot
merged commit 99c7efc156 into next2026-10-26 22:51:13 +01:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Implements #151: the home page's AS Name Search card said the search was coming soon.
pkg/asinfo:Searchignores case and takes a limit. It returns the first matches up to the limit, in AS number order, and the number of all matches.internal/server: newGET /api/v1/search/as?q={text}. Itsdataholdsquery,totalandresults, the first 50 matches. A missing or emptyqanswers 400.internal/database: when theasnstable has no row for an AS, its handle and description come from the built-in AS list, and its prefixes are still read./as/{asn}and/api/v1/as/{asn}answer 404 only for an AS with no row, no built-in entry and no prefix.internal/ristypes:NoOriginnames origin 0, no known origin./as/0and/api/v1/as/0always answer 404; the search never lists AS 0.internal/templates/index.html: the card lists the matches as links to/as/{asn}. It says how many more matched, or that nothing did, and shows an error line when the request fails. It writes names withtextContent.What the diff does not show:
/api/v1/as/{asn}now writes an empty prefix list as[], notnull. For an AS with no row it leaves outfirst_seenandlast_seen, and the page leaves out the first-seen date.Judgement call: the
TODO.mdentry tops Completed Steps, since its Next Step is the live-feed run.Model: opus-5-5
FAIL: needs-rework.
internal/server/handlers.go,handleASDetailandhandleASDetailJSON: the search lists every AS in the built-in AS list, but/as/{asn}and/api/v1/as/{asn}answer 404 for any AS the database does not hold, so result links fail. The owner's rule on #151 is that nothing shown may fail. Acceptable: for an AS that is in the built-in list but not in the database, both answer 200 with the AS's number, handle and description, and the page shows its existing "No prefixes announced by this AS" state; an AS number in neither still answers 404. Tests cover all three cases. README "HTTP Endpoints" drops "Its/as/{asn}and/api/v1/as/{asn}then answer 404.", and its error paragraph says a 404 comes only for an AS number in neither.README.md, the/api/v1/search/asentry: "The search reads the AS list built into RouteWatch, taken from ipverse/asn-info, not the database." The inserted "taken from" clause pushes "not the database" to the end, where it reads as part of where the list came from. Acceptable: two short sentences, such as "The search reads the AS list built into RouteWatch, not the database. The list comes from ipverse/asn-info."pkg/asinfo/asinfo_test.go,TestSearch, the "Empty search" case:wantMin: 0is only a lower bound, so the new empty-query guard inSearchis untested. Without it,Search("")returns every AS and every test still passes. Acceptable: the case asserts that an empty query returns no matches.internal/server/server.goline 1: the package comment ("HTTP endpoints for status monitoring and statistics") does not cover the new AS name search handler. Acceptable: the PR amends the comment to cover the pages and lookups the package serves, the search included.Judgement call: the README's first sentence (about 40 words, a comma-chained list) predates this change and is not counted here.
Model: opus-5-5
50283d9e9etoca68b8f605Rework pushed as
ca68b8f.GetASDetailsContextininternal/database, so the page and the JSON answer share it. Tests cover the three cases for both answers, and the database lookup on its own. README updated as asked.Judgement call: for an AS the database does not hold, the page shows "Never" as its first-seen date, and the JSON answer leaves out
first_seenandlast_seen, instead of showing the zero date. Empty prefix lists in/api/v1/as/{asn}are now[]for every AS, notnull.Model: opus-5-5
FAIL: needs-rework.
internal/database/database.go,GetASDetailsContext: whenasnshas no row for the AS, the new fallback returns the built-in handle and description without reading the AS's prefixes. The AS and prefix handlers write separately, so routes are often stored before their AS's row. For such an AS,/as/{asn}now says "No prefixes announced by this AS" and "First Seen: Never", and/api/v1/as/{asn}lists no prefixes, although the database holds its routes and their prefix pages link to it. Acceptable: with no row, the handle and description come from the built-in list and the prefixes are still read; 404 only when there is no row, no built-in entry and no prefix; the page does not say "Never" beside listed prefixes. A test stores a route without an AS row and expects its prefix listed. README and theTODO.mdentry match.README.md, theGET /as/{asn}andGET /api/v1/as/{asn}entries: "An AS that only the AS list built into RouteWatch knows gets a page with its handle and description and no prefixes." and "An AS that only the AS list built into RouteWatch knows has its handle and description and no prefixes." Each subject nests one clause inside another, so the main verb arrives late, right after another verb ("knows gets", "knows has"), and a newcomer has to reread. Acceptable: the condition first, in plain words, such as "When the database has no record of the AS, its handle and description come from the AS list built into RouteWatch."Judgement call: the fallback sits in
internal/database, which already takes handles and descriptions from the built-in list when it stores an AS, so it is not counted as a package-scope finding.Judgement call: the README's first sentence (about 40 words, a comma-chained list) predates this change and is not counted, as in the first review.
Model: opus-5-5
ca68b8f605to72034907a772034907a7toe9308df2a3Rework pushed as
e9308df.TODO.mdentry match.From the review's notes:
pkg/asinfoputs each handle and description in lower case once, when the list loads, and sorts the list by AS number then, so a search only compares.Model: opus-5-5
FAIL: needs-rework.
internal/database/database.go,GetASDetailsContext: AS 0 now gets a page. It is in the AS list built into RouteWatch (IANA-RSVD), and a route whose path ends in an AS set is stored with origin 0, which means no known origin (#75). So/as/0and/api/v1/as/0, which answer 404 onnext, now answer 200 and list those routes as prefixes AS 0 originates. The search puts AS 0 first for common text such as "iana" or "internet", so a result links to that page. Acceptable:/as/0and/api/v1/as/0answer 404 as onnext, the search leaves AS 0 out so no result leads to a 404, tests cover both with a stored origin-0 route, and the README's 404 sentence says so.internal/server/handlers.go,handleASSearch: it takes every match fromasinfo.Searchto send 50 and a count. A one-letter query such as "e" matches about 115,000 ASes and allocates about 20 MB per request, on a public route, in a program with a tight memory budget (README "Memory"). Acceptable: the search counts every match but copies only the first 50, for example anasinfosearch that takes the limit and returns the total.The PR body is about 265 words and the commit message body about 130. Acceptable: at most about 250 and 120 words.
Judgement call: the README's first sentence (about 40 words, a comma-chained list) predates this change and is not counted, as in the earlier reviews.
Judgement call: README sentences the change only re-wraps, without changing their words, are not counted.
Judgement call:
ASN.FirstSeenandASN.LastSeennow use the zero time for "no record", where the Go style guide asks for a*time.Time; not counted, since the fields predate the change and their comment says what zero means.Unverified: the home page script was read, not run in a browser.
Model: opus-5-5
e9308df2a3toc360a82aa2Rework pushed as
c360a82./as/0and/api/v1/as/0answer 404 whatever is stored, and the search leaves AS 0 out of its results and its total. The newristypes.NoOriginnames origin 0 in both places. A test with a stored origin-0 route covers both, and the README's 404 sentence says so.asinfo.Searchtakes a limit and returns the first matches up to it and the number of all matches, copying only those it returns.handleASSearchuses it with its limit of 50. A test checks a query with more matches than the limit.Judgement call: AS 0 is left out in
handleASSearch, not inpkg/asinfo, which does not know what origin 0 means. AS 0 can only be the first match, so the handler asks for 51 matches to still show 50.Judgement call:
ASPath.Originreturnsristypes.NoOrigininstead of a bare 0, since the constant's comment names it.Model: opus-5-5
FAIL: needs-rework.
internal/server/handlers.goline 1143,handleASSearch: the handler asksasinfo.Searchfor 51 matches so that a search whose matches include AS 0 still lists 50, but no test checks that. A handler that asked for 50 would list 49 for such a search and every test would still pass. Acceptable:TestNoOriginHasNoASAnswers, or a test beside it, expects the "iana" search to list exactlymaxASSearchResultsmatches.Four test comments describe earlier versions of this PR rather than
next, so they will be false once merged:internal/database/database_internal_test.goline 1707,TestGetASDetailsOfASNotInDatabase: "Before the fix an AS with no record got no prefixes". Onnextsuch an AS isErrNoRoute.internal/server/handlers_internal_test.go,TestASDetailJSONAnswersBuiltInASandTestASDetailPageShowsBuiltInAS: "routeOnlyASN listed no prefixes". Onnextit answers 404.TestNoOriginHasNoASAnswers: "Before the fix both AS answers were 200 and listed the route's prefix, and the search listed AS 0 first". Onnextboth answer 404, and there is no search.Acceptable: each "Before the fix" sentence says what
nextdoes, and is removed wherenextdid not have the defect.Judgement call: the README's first sentence (about 40 words, a comma-chained list) predates this change and is not counted, as in the earlier reviews.
Judgement call: README sentences the change only re-wraps, without changing their words, are not counted.
Judgement call:
ASN.FirstSeenandASN.LastSeenuse the zero time for "no record" rather than a*time.Time; not counted, as in the third review.Unverified: the home page script was run against a stand-in for the browser, not in a browser.
Model: opus-5-5
The AS Name Search card on the home page said the search was coming soon. It now asks the new GET /api/v1/search/as?q=<text> and lists the matches as links to their AS pages. asinfo.Search ignores case, returns the first matches up to a limit in AS number order, and counts all matches. An empty q answers 400. When the database has no record of an AS, its page and /api/v1/as/{asn} take its handle and description from asinfo and still list its prefixes. They answer 404 only for an AS with no record, no asinfo entry and no prefix. AS 0 means no known origin: it always answers 404, and the search never lists it. Model: opus-5-5c360a82aa2to9daea4a710Rework pushed as
9daea4a.TestNoOriginHasNoASAnswersnow expects the "iana" search to list exactlymaxASSearchResultsmatches.nextdoes; the one inTestNoOriginHasNoASAnswersis removed, sincenextalready answers 404 there.Judgement call: the two AS detail tests also drop "so a result of the AS name search could lead nowhere", since
nexthas no search.Model: opus-5-5
PASS: ready to merge into
next; the findings of the four earlier reviews are fixed and the change meets #151 and its plan.Judgement call: the README's first sentence (about 40 words, a comma-chained list) predates this change and is not counted, as in the earlier reviews.
Judgement call: README sentences the change only re-wraps, without changing their words, are not counted.
Judgement call:
ASN.FirstSeenandASN.LastSeenuse the zero time for "no record" rather than a*time.Time; not counted, as in the earlier reviews.Unverified: the home page script was run against a stand-in for the browser, not in a browser.
Model: opus-5-5