Звіт у форматі JSON машиночитний
{
"data": {
"repo": {
"topics": [
"dynamic-web",
"dynamic-webpages",
"go",
"golang",
"websocket",
"websockets",
"echo-framework",
"react",
"ui",
"frontend",
"web"
],
"is_fork": false,
"size_kb": 4245,
"has_wiki": true,
"homepage": null,
"languages": {
"Go": 1283245,
"CSS": 291,
"JavaScript": 29022
},
"pushed_at": "2026-07-23T18:01:30Z",
"created_at": "2022-07-25T11:04:31Z",
"owner_type": "User",
"updated_at": "2026-07-23T17:58:43Z",
"description": "Javascript and WebSockets enabling dynamic webpages",
"is_archived": false,
"is_disabled": false,
"license_spdx": "MIT",
"default_branch": "main",
"license_spdx_raw": "MIT",
"primary_language": "Go",
"significant_languages": [
"Go"
]
},
"owner": {
"blog": "https://linkdata.se/",
"name": "Johan Lindh",
"type": "User",
"login": "linkdata",
"company": null,
"location": "Sweden",
"followers": 10,
"avatar_url": "https://avatars.githubusercontent.com/u/2185977?v=4",
"created_at": "2012-08-20T22:03:58Z",
"is_verified": null,
"public_repos": 44,
"account_age_days": 5088
},
"license": {
"state": "standard",
"spdx_id": "MIT",
"raw_spdx": "MIT",
"file_present": true,
"scorecard_found": true,
"profile_has_license": true
},
"activity": {
"releases": [
{
"tag": "v0.600.0",
"kind": "minor",
"published_at": "2026-06-23T07:37:08Z"
},
{
"tag": "v0.500.0",
"kind": "minor",
"published_at": "2026-06-01T08:16:34Z"
},
{
"tag": "v0.400.0",
"kind": "minor",
"published_at": "2026-04-15T13:42:07Z"
},
{
"tag": "v0.300.0",
"kind": "minor",
"published_at": "2026-04-01T10:54:21Z"
},
{
"tag": "v0.205.0",
"kind": "minor",
"published_at": "2026-03-26T11:47:28Z"
},
{
"tag": "v0.204.0",
"kind": "minor",
"published_at": "2026-03-25T13:34:47Z"
},
{
"tag": "v0.203.0",
"kind": "minor",
"published_at": "2026-03-24T10:02:01Z"
},
{
"tag": "v0.202.0",
"kind": "minor",
"published_at": "2026-03-18T14:28:14Z"
},
{
"tag": "v0.201.0",
"kind": "minor",
"published_at": "2026-02-18T14:33:51Z"
},
{
"tag": "v0.200.0",
"kind": "minor",
"published_at": "2026-02-12T10:20:35Z"
},
{
"tag": "v0.118.0",
"kind": "minor",
"published_at": "2025-10-06T11:47:06Z"
},
{
"tag": "v0.117.0",
"kind": "minor",
"published_at": "2025-08-26T08:15:12Z"
},
{
"tag": "v0.116.0",
"kind": "minor",
"published_at": "2025-08-25T07:08:05Z"
},
{
"tag": "v0.115.0",
"kind": "minor",
"published_at": "2025-08-20T08:03:20Z"
},
{
"tag": "v0.114.0",
"kind": "minor",
"published_at": "2025-08-19T09:38:36Z"
},
{
"tag": "v0.113.0",
"kind": "minor",
"published_at": "2025-08-18T11:19:37Z"
},
{
"tag": "v0.112.0",
"kind": "minor",
"published_at": "2025-08-14T10:40:37Z"
},
{
"tag": "v0.111.0",
"kind": "minor",
"published_at": "2025-08-06T09:56:22Z"
},
{
"tag": "v0.110.0",
"kind": "minor",
"published_at": "2025-07-07T10:05:18Z"
},
{
"tag": "v0.109.0",
"kind": "minor",
"published_at": "2025-03-12T10:16:19Z"
},
{
"tag": "v0.108.0",
"kind": "minor",
"published_at": "2025-02-17T10:52:38Z"
},
{
"tag": "v0.107.0",
"kind": "minor",
"published_at": "2025-02-17T10:24:43Z"
},
{
"tag": "v0.106.5",
"kind": "patch",
"published_at": "2025-02-17T10:10:59Z"
},
{
"tag": "v0.106.3",
"kind": "patch",
"published_at": "2025-02-14T10:58:37Z"
},
{
"tag": "v0.106.2",
"kind": "patch",
"published_at": "2025-02-14T10:43:54Z"
},
{
"tag": "v0.106.0",
"kind": "minor",
"published_at": "2025-01-31T11:19:22Z"
},
{
"tag": "v0.105.0",
"kind": "minor",
"published_at": "2024-12-18T07:12:17Z"
},
{
"tag": "v0.104.0",
"kind": "minor",
"published_at": "2024-12-17T13:17:20Z"
},
{
"tag": "v0.103.0",
"kind": "minor",
"published_at": "2024-12-16T07:15:35Z"
},
{
"tag": "v0.102.0",
"kind": "minor",
"published_at": "2024-12-13T12:48:42Z"
},
{
"tag": "v0.101.0",
"kind": "minor",
"published_at": "2024-12-09T07:35:11Z"
},
{
"tag": "v0.100.0",
"kind": "minor",
"published_at": "2024-12-06T10:29:01Z"
},
{
"tag": "v0.99.1",
"kind": "patch",
"published_at": "2024-12-05T12:07:31Z"
},
{
"tag": "v0.99.0",
"kind": "minor",
"published_at": "2024-12-03T09:52:11Z"
},
{
"tag": "v0.98.0",
"kind": "minor",
"published_at": "2024-12-03T09:27:56Z"
},
{
"tag": "v0.97.0",
"kind": "minor",
"published_at": "2024-12-03T06:46:48Z"
},
{
"tag": "v0.96.0",
"kind": "minor",
"published_at": "2024-11-29T10:06:31Z"
},
{
"tag": "v0.95.0",
"kind": "minor",
"published_at": "2024-11-29T09:31:45Z"
},
{
"tag": "v0.94.0",
"kind": "minor",
"published_at": "2024-11-29T09:27:27Z"
},
{
"tag": "v0.93.0",
"kind": "minor",
"published_at": "2024-11-25T08:00:49Z"
},
{
"tag": "v0.92.0",
"kind": "minor",
"published_at": "2024-11-21T11:24:22Z"
},
{
"tag": "v0.91.0",
"kind": "minor",
"published_at": "2024-11-21T09:43:03Z"
},
{
"tag": "v0.90.0",
"kind": "minor",
"published_at": "2024-11-21T09:07:24Z"
},
{
"tag": "v0.89.0",
"kind": "minor",
"published_at": "2024-11-21T08:44:59Z"
},
{
"tag": "v0.88.0",
"kind": "minor",
"published_at": "2024-11-21T08:27:45Z"
},
{
"tag": "v0.87.0",
"kind": "minor",
"published_at": "2024-11-20T13:05:05Z"
},
{
"tag": "v0.86.0",
"kind": "minor",
"published_at": "2024-11-14T13:03:15Z"
},
{
"tag": "v0.85.0",
"kind": "minor",
"published_at": "2024-11-14T12:52:42Z"
},
{
"tag": "v0.84.0",
"kind": "minor",
"published_at": "2024-11-13T09:59:51Z"
},
{
"tag": "v0.83.0",
"kind": "minor",
"published_at": "2024-11-11T10:51:01Z"
},
{
"tag": "v0.82.0",
"kind": "minor",
"published_at": "2024-11-11T09:58:53Z"
},
{
"tag": "v0.81.0",
"kind": "minor",
"published_at": "2024-11-08T12:39:48Z"
},
{
"tag": "v0.80.0",
"kind": "minor",
"published_at": "2024-11-06T08:01:31Z"
},
{
"tag": "v0.79.0",
"kind": "minor",
"published_at": "2024-09-24T12:02:42Z"
},
{
"tag": "v0.78.2",
"kind": "patch",
"published_at": "2024-09-18T11:21:47Z"
},
{
"tag": "v0.78.1",
"kind": "patch",
"published_at": "2024-09-18T11:14:51Z"
},
{
"tag": "v0.78.0",
"kind": "minor",
"published_at": "2024-09-18T10:56:41Z"
},
{
"tag": "v0.77.0",
"kind": "minor",
"published_at": "2024-08-21T07:38:26Z"
},
{
"tag": "v0.76.0",
"kind": "minor",
"published_at": "2024-08-12T08:00:37Z"
},
{
"tag": "v0.75.2",
"kind": "patch",
"published_at": "2024-06-28T09:47:32Z"
},
{
"tag": "v0.75.1",
"kind": "patch",
"published_at": "2024-06-28T09:44:59Z"
},
{
"tag": "v0.75.0",
"kind": "minor",
"published_at": "2024-06-28T09:39:08Z"
},
{
"tag": "v0.74.1",
"kind": "patch",
"published_at": "2024-06-14T09:47:01Z"
},
{
"tag": "v0.74.0",
"kind": "minor",
"published_at": "2024-06-14T09:40:05Z"
},
{
"tag": "v0.73.0",
"kind": "minor",
"published_at": "2024-06-13T10:33:33Z"
},
{
"tag": "v0.72.0",
"kind": "minor",
"published_at": "2024-06-13T07:41:39Z"
},
{
"tag": "v0.71.0",
"kind": "minor",
"published_at": "2024-05-30T10:31:51Z"
},
{
"tag": "v0.70.0",
"kind": "minor",
"published_at": "2024-05-30T07:24:39Z"
},
{
"tag": "v0.69.0",
"kind": "minor",
"published_at": "2024-05-29T09:28:00Z"
},
{
"tag": "v0.68.0",
"kind": "minor",
"published_at": "2024-05-28T06:07:45Z"
},
{
"tag": "v0.67.0",
"kind": "minor",
"published_at": "2024-05-27T09:55:05Z"
},
{
"tag": "v0.66.0",
"kind": "minor",
"published_at": "2024-05-24T11:01:35Z"
},
{
"tag": "v0.65.0",
"kind": "minor",
"published_at": "2024-05-24T06:52:32Z"
},
{
"tag": "v0.64.0",
"kind": "minor",
"published_at": "2024-05-24T06:28:15Z"
},
{
"tag": "v0.63.2",
"kind": "patch",
"published_at": "2024-04-29T10:35:53Z"
},
{
"tag": "v0.63.1",
"kind": "patch",
"published_at": "2024-04-29T10:07:42Z"
},
{
"tag": "v0.63.0",
"kind": "minor",
"published_at": "2024-04-29T05:17:33Z"
},
{
"tag": "v0.62.0",
"kind": "minor",
"published_at": "2024-04-26T12:17:02Z"
},
{
"tag": "v0.61.0",
"kind": "minor",
"published_at": "2024-04-18T09:46:38Z"
},
{
"tag": "v0.60.0",
"kind": "minor",
"published_at": "2024-04-10T08:18:27Z"
},
{
"tag": "v0.59.0",
"kind": "minor",
"published_at": "2024-03-08T08:59:57Z"
},
{
"tag": "v0.58.0",
"kind": "minor",
"published_at": "2024-03-07T11:59:05Z"
},
{
"tag": "v0.57.0",
"kind": "minor",
"published_at": "2024-02-28T12:05:24Z"
},
{
"tag": "v0.56.0",
"kind": "minor",
"published_at": "2024-02-26T11:47:52Z"
},
{
"tag": "v0.55.0",
"kind": "minor",
"published_at": "2024-02-26T10:20:05Z"
},
{
"tag": "v0.54.0",
"kind": "minor",
"published_at": "2024-02-21T12:29:48Z"
},
{
"tag": "v0.53.1",
"kind": "patch",
"published_at": "2024-02-21T09:59:30Z"
},
{
"tag": "v0.53.0",
"kind": "minor",
"published_at": "2024-02-19T12:41:15Z"
},
{
"tag": "v0.52.0",
"kind": "minor",
"published_at": "2024-02-16T11:34:14Z"
},
{
"tag": "v0.51.1",
"kind": "patch",
"published_at": "2024-02-16T08:06:29Z"
},
{
"tag": "v0.51.0",
"kind": "minor",
"published_at": "2024-02-15T12:37:42Z"
},
{
"tag": "v0.50.0",
"kind": "minor",
"published_at": "2024-02-14T11:57:05Z"
},
{
"tag": "v0.49.0",
"kind": "minor",
"published_at": "2024-02-12T11:49:00Z"
},
{
"tag": "v0.48.0",
"kind": "minor",
"published_at": "2024-02-12T08:45:13Z"
},
{
"tag": "v0.47.0",
"kind": "minor",
"published_at": "2024-02-08T09:23:04Z"
},
{
"tag": "v0.46.2",
"kind": "patch",
"published_at": "2024-02-02T06:57:29Z"
},
{
"tag": "v0.46.1",
"kind": "patch",
"published_at": "2024-01-29T11:20:18Z"
},
{
"tag": "v0.46.0",
"kind": "minor",
"published_at": "2024-01-29T10:08:58Z"
},
{
"tag": "v0.45.1",
"kind": "patch",
"published_at": "2024-01-25T11:16:14Z"
},
{
"tag": "v0.45.0",
"kind": "minor",
"published_at": "2024-01-25T10:42:49Z"
}
],
"recent_commits": [
{
"oid": "a71480a083e0efa28f6bcb5db716d93699dca694",
"body": "…et subscribes (#220)\n\nFixes #215. Session.Close now queues one Reload frame onto each associated Request's wsQueue (delivered when its WebSocket connects) and broadcasts a key-targeted what.Update purely as a wake-up for an already-running process loop, so a Request still in its pending window is r\n[…]\noad classification is tightened so only the internal nil-destination dirty-render tick is droppable; every addressed message, including tag- and key-targeted Updates, fails-fast an overloaded Request.",
"is_bot": false,
"headline": "fix: reload associated Requests on Session.Close before their WebSock…",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-23T17:58:38Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "1e0d9dbd86c8b09aa538aea01698ffac3f080014",
"body": "…casts (#211)\n\n* fix: remove the Jaws.Replace broadcast method (#199)\n\nJaws.Replace broadcast a what.Replace frame that swapped each matching DOM\nnode for the supplied HTML but left the corresponding server-side Element\nregistered. The replacement HTML normally carried no JaWS id attribute, so\nafter\n[…]\nx malformed plural doc link in Jaws.Broadcast\n\nThe [Element]s doc link rendered literally because the trailing s made it\nan invalid doc-link target. Rephrase as \"matched [Element] values\" so it\nlinks.",
"is_bot": false,
"headline": "fix: close #199 — remove Jaws.Replace and reject Replace/Remove broad…",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-22T20:44:23Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "6fd13a23986c5b01d05376416b729d75e12c97c7",
"body": "…d (#214)\n\nTagExpand's cycle detector identified active slice nodes by first-element\npointer alone, so aliased views into the same backing array with different\nlengths (or, for a named slice TagGetter that observes cap, different\ncapacities) were mistaken for cycles. The nested slice was skipped, si\n[…]\n.\n\nCompare pointer, length and capacity, which together fully identify a slice\nheader. A genuine self-referential slice re-enters with an identical header and\nis still detected as a cycle.\n\nFixes #203",
"is_bot": false,
"headline": "fix: identify slice nodes by pointer, length and capacity in TagExpan…",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-22T19:57:00Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "852aa2bd5e472245dade5b836ffc2bbfd7d6e4c6",
"body": "…205)\n\nAddTemplateLookuper validates only runtime comparability, but a value that\nis Go-comparable yet not equal to itself — a struct carrying a NaN — passes\nthat check while never matching in slices.Contains or the removal predicate.\nAdds of such a value are not deduplicated and RemoveTemplateLooku\n[…]\nout removing it.\n\nDocument on AddTemplateLookuper and RemoveTemplateLookuper that the lookuper\nmust compare equal to itself, and add TestTemplateLookuperNonReflexiveNotRemovable\nto guard the behavior.",
"is_bot": false,
"headline": "docs: document reflexive-equality requirement for TemplateLookuper (#…",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-22T19:35:08Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "591331fc937587e1d27e2e508023062defca2fd8",
"body": "The Date widget renders any bound year, but its input handler parses with\nthe fixed-width \"2006-01-02\" layout, so only years 1..9999 round-trip: a\nbound year of 10000 or more renders with five digits yet a browser edit of\nthat value fails to parse, returning an event error and leaving the bound\nvalue unchanged.\n\nDocument the supported year range on InputDate.JawsInput, the InputDate type,\nand NewDate, and add TestInputDate_YearRangeRoundTrip to guard the boundary.",
"is_bot": false,
"headline": "docs(ui): document Date widget four-digit-year round-trip limit (#204)",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-22T19:32:06Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "2d84b62247c068907e408442308cfeeca9ad1e37",
"body": "html.EscapeString leaves a raw carriage return unescaped, so a multiline\nattribute value read back from the DOM had its CR turned into LF (browser\ninput-stream preprocessing rewrites raw CR and CRLF to LF before tokenization).\n\nAppendAttrValue now encodes each CR as the numeric character reference &\n[…]\nnts callers to AppendAttrValue.\n\nTests model getAttribute and HTMLTextAreaElement.value semantics with the\nstandard library html.UnescapeString, keeping the dependency surface stdlib-only.\n\nFixes #202",
"is_bot": false,
"headline": "fix: round-trip carriage returns in HTML attribute values (#202)",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-22T19:25:12Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "83c8556127bb7e966d930487da2d380cc15be478",
"body": "An HTML range input carries an implicit browser domain of min=\"0\", max=\"100\", step=\"1\". Range emits none of those attributes, so a bound value outside that domain or off the step grid is silently clamped and rounded by the browser and echoed back through the setter, overwriting server state.\n\nDocume\n[…]\ning that callers must supply explicit min/max/step attributes as params when the defaults do not cover the bound value's domain, and add a test guarding that those params render verbatim.\n\nCloses #201",
"is_bot": false,
"headline": "docs(ui): document Range browser-default bounds (#201)",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-22T18:58:20Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "5a29406020be7df65a9e783b6821cdd64f6c64b9",
"body": "fix: reject the framework-owned \"id\" attribute in attribute helpers",
"is_bot": false,
"headline": "Merge pull request #210 from linkdata/fix/attr-helpers-reserved-id-198",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-22T17:51:44Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "78c13ccef74ad1c7845e37986f93c1791cbe9926",
"body": "- Fix the flaky normal-attribute control test: use a non-running\n jw.NewRequest(nil) whose process loop cannot drain wsQueue underneath\n the assertion, instead of newTestRequest.\n- Wrap only the rejected call in the debug-panic recovery so the\n error-identity and queue assertions also run under -race.\n- Add a node-backed jaws.js behavioral test covering case-insensitive\n SAttr/RAttr rejection and that jawsMessage keeps processing later\n orders in the same frame after the guard throws.",
"is_bot": false,
"headline": "test: address review of the id-attribute rejection tests",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-22T17:48:05Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c7d6a874d522cbf14821857811298d1488d7cf1a",
"body": "Element.SetAttr/RemoveAttr and Jaws.SetAttr/RemoveAttr accepted any\nattribute name, including \"id\". A managed DOM node is addressable only by\nits JaWS Jid, and every wire command resolves its target via\ndocument.getElementById, so setting or removing \"id\" stranded the\nserver-side Element with an unr\n[…]\nthe\nnew ErrReservedAttribute sentinel via reportMisuse and sending nothing,\nmirroring how Jaws.Insert guards a negative child index. Add a client-side\nguard in jaws.js as defense in depth.\n\nFixes #198",
"is_bot": false,
"headline": "fix: reject the framework-owned \"id\" attribute in attribute helpers",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-22T17:35:16Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "56367742e95ccf0ef78695ccf7eae60b8d7d9cc7",
"body": "…flow-197\n\nfix: gate full tag rendering behind the debug/race build tags (#197)",
"is_bot": false,
"headline": "Merge pull request #209 from linkdata/fix/tagstring-cyclic-stack-over…",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-22T17:22:48Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "16ce510fadc0551070004c1d179f03da6dc2c2c7",
"body": "TagString formatted tags with fmt's %#v, and Message.String / Element.String\nformatted their tag Dest / tag list with %v. Handing an arbitrary tag to fmt is\nunsafe: fmt has no cycle or size bound, so a self-referential or oversized tag\noverflows the stack or exhausts memory. This is not confined to \n[…]\nevelopment and -race builds keep full detail.\n\nBecause -race and -tags debug select the debug renderer, CI also runs the suite\nwithout them so the crash-safe release path is exercised too.\n\nFixes #197",
"is_bot": false,
"headline": "fix: gate full tag rendering behind the debug/race build tags",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-22T17:17:47Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "11a6eea21362bd9e02fc03f18203d8e90ec5ab11",
"body": "fix: don't render nil JsVar Ptr through value-receiver callbacks (#196)",
"is_bot": false,
"headline": "Merge pull request #208 from linkdata/fix/jsvar-nil-ptr-render-lock-196",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T20:27:01Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "a7dd5e7a1255c251eda0a8dc32022251f2307a25",
"body": "Rendering a JsVar bound to a nil pointer passed the typed-nil Ptr to\nElement.ApplyGetter, which saw a non-nil interface and invoked a\nvalue-receiver tag.TagGetter (or InitHandler) through the nil pointer,\npanicking. Because the write lock was released with a bare Unlock rather\nthan a deferred one, t\n[…]\n Ptr is nil. A nil Ptr now renders with no initial\ndata and no bound-value tag, as NewJsVar documents, and a panic from a\nsupported callback or from marshaling can no longer leak the lock.\n\nFixes #196",
"is_bot": false,
"headline": "fix: don't render nil JsVar Ptr through value-receiver callbacks",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T20:21:57Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "7ac9bbf66378e4fc408477e236c2b445b7d6fb81",
"body": "refactor: consolidate Request lifecycle flags into an atomic int32 state",
"is_bot": false,
"headline": "Merge pull request #207 from linkdata/fix/request-lifecycle-state",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T20:16:02Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c0354fdcd9c50d7e3e017d0b7d5b6c4229c8addc",
"body": "The strengthened Close-race test passed onPanic func(any){}, discarding any panic\nrecovered from TestServe's process/recycle goroutine; only the synchronous setup\npanic was asserted. A panic escaping recycle — notably finishLocked's terminal-state\nassertion after a terminal->running resurrection — w\n[…]\nection: forcing an\ninconsistent terminal state before recycle makes finishLocked's assertion escape and\nis now caught (per-element handler panics remain recovered inside process and are out\nof scope).",
"is_bot": false,
"headline": "test: capture TestServe onPanic in the Close-race test",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T20:12:56Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "f550239e46e3d9acdd021c482fa474247879d89a",
"body": "…se-race test\n\n- finishLocked doc: state that both rq.mu and jw.mu are required, and why. claim\n runs its reqPending->reqClaimed CAS under rq.mu while startServe runs its\n reqClaimed->reqRunning CAS under jw.mu (not rq.mu), so both locks are needed to\n keep finishLocked's read-then-store atomic a\n[…]\ns a terminal->\n running resurrection regression.\n\n- TestFinishLockedTerminalStatePanicsInDebug: hold jw.mu as well as rq.mu, matching\n the documented finishLocked contract and production lock order.",
"is_bot": false,
"headline": "refactor: address review — accurate finish/release docs, stronger Clo…",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T20:04:22Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "45fa1d2df0663f8aa03122fe8ef0c1df76f3aee1",
"body": "Replace the three separate lifecycle flags on Request (registered bool,\nrunning atomic.Bool, claimed atomic.Bool) with a single reqState int32 held\nin Request.state, making the transitions explicit and race-safe:\n\n reqUnclaimable -> (terminal; created after Jaws.Close)\n reqPending -> reqClaime\n[…]\ncycle benchmarks), old = separate flags, new = int32 state:\n\n B/op geomean -3.65% (e.g. 401 -> 385, 416 -> 400)\n sec/op geomean -2.51%\n allocs/op geomean 0.00% (unchanged)",
"is_bot": false,
"headline": "refactor: consolidate Request lifecycle flags into an atomic int32 state",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T19:48:10Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "e23d53f982535a17bf22adc559160dd00d9cefcb",
"body": "Baseline benchmark (no behavior change) covering NewRequest -> UseRequest ->\nstartServe -> recycle, which the existing create/recycle and high-water benchmarks\ndo not exercise. Landed before the lifecycle-state consolidation so benchstat has\nbefore/after samples for the claim/start path.",
"is_bot": false,
"headline": "test: add BenchmarkRequestClaimStartFinish for the lifecycle CAS path",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T19:06:27Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "395e7ec544e4c21d9f2583d284d0c434134a6469",
"body": "fix: never reuse Request identities, pool only buffers (#195)",
"is_bot": false,
"headline": "Merge pull request #206 from linkdata/fix/195-stable-request-identity",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T18:13:06Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "e569de99011734d347376eeb462db59cd9a21fc1",
"body": "…key guarantee\n\nThe Request godoc no longer claims all identity-targeted operations are never\nretargeted. It defines the never-reused identity as the *Request pointer (GC'd,\nnever handed out again, so a retained pointer never aliases another connection), and\nscopes the operational guarantee to desti\n[…]\nistered\nRequests: a Request created by NewRequest after Jaws.Close is unregistered and installs\nno tombstone, so two post-close calls could receive the same key — harmless, since\nneither is claimable.",
"is_bot": false,
"headline": "docs: scope exported Request identity to the pointer; qualify README …",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T18:05:02Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "0580bf47a75459c3559be799ef4831240b32bdb0",
"body": "…ing + destKey resolution\n\nThe previous wording claimed \"pointer-derived operations never reach a different\nconnection\", which is false: Request.Dirty dirties matching Elements on other live\nRequests by design, and an Alert/Redirect message queued before completion carries a\nkey.Key destination that\n[…]\niases another connection, and destKey returns zero once the Request has finished so\nit cannot hand back the finished destination — and explicitly note it is not a\nblanket pointer-derived-safety claim.",
"is_bot": false,
"headline": "docs: narrow destKey's post-completion guarantee to pointer non-alias…",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T17:58:10Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "0b338d5299b62a5920eb4ce17721cc29934209cb",
"body": "A key VALUE (a copied key.Key, a queued wire.Message.Dest, or a browser /jaws/<key>\nURL) can outlive the *Request. While the finished Request stays reachable its key is\nheld reserved by a nil tombstone, so a stale destination matches nothing; but after\nthe Request is collected the tombstone is remov\n[…]\ns lifetime would make the absolute guarantee\nhold, but at the cost of an unbounded jw.requests leak (one dead entry per request\never created), so the cleanup-based, correctly-scoped guarantee is kept.",
"is_bot": false,
"headline": "docs: scope stale key-value matching to the tombstone lifetime",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T17:51:43Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "926e11668b777da5706e6ed791fc32d1b8ef6eea",
"body": "…nt to reachability\n\nTestEarlyCallbackPreservesInitialRenderIdentity now captures the key before the early\ncallback and asserts it is unchanged afterward, instead of merely checking it is\nnonempty; the comment says the key stays stable and nonzero (the finished Request is\nunregistered), not \"valid\".\n[…]\nis collected the\ntombstone is cleaned up and the key becomes eligible for random reuse, by which point\nno stale destination referencing it can remain. A wantMessage test comment is\nlikewise qualified.",
"is_bot": false,
"headline": "docs: assert key stability in the #195 test; scope key non-reassignme…",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T17:34:02Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "f57d5b7d8779deb7ebbec7d52dc5e8831b4ba425",
"body": "Documentation and test-name accuracy.\n\nQualify the early-teardown contract: clearing the collections forgets the\nalready-rendered elements and tags (a later GetElements finds nothing), but a\nsubsequent NewElement repopulates rq.elems and is again findable by Jid. Correct the\nlocking claim — the lock\n[…]\ninished, or detached from this Session\". The\nTestSession_ProducersSkipRecycled fixture comment now says the snapshot is taken\nunder sess.mu and processed after releasing it, not taken after unlocking.",
"is_bot": false,
"headline": "docs: qualify post-teardown lookup/locking; finish lifecycle terminology",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T17:25:57Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "df868e176f72b706436093311347d1f10ee645e3",
"body": "…op rebind fixture\n\nDocumentation and test-fixture accuracy.\n\nEarly teardown clears the reusable collections (element list, tag map, dirt list,\nmessage queue) while leaving the individual Element fields and the Jid counter\nintact. Describe it as race-safe rather than non-destructive: releaseBuffersL\n[…]\ntached Request (its own nonzero key, session == nil), which is what the\nproducers' skip branch actually guards; the real snapshot-vs-finish race is covered\nby TestSessionCloseDoesNotReachLaterRequest.",
"is_bot": false,
"headline": "docs: describe early teardown as race-safe (not fully preserving); dr…",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T17:18:05Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "986ca7a36993d0b7d85049754bb8738248c95f5b",
"body": "…Request guarantee\n\nDocumentation-only, refining the lifecycle contract.\n\nThe Request type doc no longer equates the end of the initial HTTP render with the\nRequest finishing: the render handler returning normally leaves the Request pending\nuntil UseRequest claims it for the WebSocket. The Request f\n[…]\ns its buffers (retirement preserves them); the wantMessage and\nProducersSkip test comments no longer say recycle zeroes JawsKey (the key is\npreserved — only registered flips and destKey returns zero).",
"is_bot": false,
"headline": "docs: separate pointer ownership from completion; scope the finished-…",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T17:09:03Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "0f9204e57fc2e2994ad83981cb392faeb9c5a5c7",
"body": "Documentation-only. The Request type doc no longer claims lifecycle completion\nalways releases buffers and leaves empty, useless collections: it distinguishes\ncompletion after WebSocket serving (which releases buffers to the pool) from\nretirement of an unclaimed Request (which preserves Elements and\n[…]\ne pool reusing a *Request: recycle now tombstones the key and only buffers are\npooled, so those tests exercise finished/unregistered requests and distinct later\nidentities rather than reused pointers.",
"is_bot": false,
"headline": "docs: correct Request lifecycle contract (completion vs retirement)",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T16:18:01Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "1e4396620b591586d0ca97043b65d564664db180",
"body": "Third review round.\n\nreleaseBuffersLocked clears only the live length of the reusable buffers, not their\nfull capacity. Every path that shrinks a buffer already zeroes the vacated entries\n(getSendMsgs and drainTailScript for wsQueue, makeUpdateList for todoDirt,\nslices.DeleteFunc for elems), so the \n[…]\n The README drops the removed render gate and, in the\nmaintainer checklist, distinguishes completion (releases buffers) from non-running\nretirement (which preserves them for the initial HTTP handler).",
"is_bot": false,
"headline": "fix: clear only live buffer length; add teardown-race regression tests",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T15:49:38Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "4ab6eaf62832be0f4e9d7539aef4cd429b5da9c9",
"body": "Second review round.\n\nRemove the initial-request-context render gate. The request context is not a\nreliable render-completion barrier: it is canceled when the client connection\ncloses (before the handler returns) and a detached/background context stays live\nafter it returns, so the gate could admit \n[…]\ncontext render still returns 204 and records\nErrJavascriptDisabled; TestReleaseBuffersLockedZeroesWsQueue drains to zero length\nbefore releasing to prove the backing array is cleared through capacity.",
"is_bot": false,
"headline": "fix: drop unreliable render gate; clear queue capacity; correct docs",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T15:28:15Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "b9d93db4e3ad723ae2b26c7bc05d9ed275e78939",
"body": "…stone\n\nFollow-up to the identity-stable change, addressing four review findings.\n\nTeardown no longer mutates render-visible state: releaseBuffersLocked stops\nclearing Element.ui/handlers and resetting lastJid, which raced an initial\nrenderer still inside JawsRender (those fields are read lock-free)\n[…]\nd-reuse model.\nTests decouple the initial-render request from the WebSocket request (distinct in\nproduction) and cover the render gate, the forced-key tombstone, and a live\nJawsRender racing teardown.",
"is_bot": false,
"headline": "fix: address review — render gate, non-destructive teardown, key tomb…",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T15:01:22Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "6c4a14fb6565e1f475a7a676bc9a55d557c6a7ef",
"body": "Jaws.ServeHTTP could recycle a Request while its initial HTTP renderer\nstill held the pointer: an early or malformed GET /jaws/<key> claimed a\nstill-rendering Request, failed the WebSocket upgrade, and the deferred\nstopServe cleared the Request and returned it to a sync.Pool. Once reused\nfrom the po\n[…]\n remain pooled, so the change adds only one small\nRequest allocation per request: negligible for real pages and vanishing at\n1000 elements (BenchmarkRequestLifecyclePooling, impl=pooled).\n\nFixes #195.",
"is_bot": false,
"headline": "fix: never reuse Request identities, pool only buffers (#195)",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T13:58:53Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "634f8c9eab4c8bdfcd192b74bd5b0c51e12e45ee",
"body": "fix: require \"type/\" prefix when matching MIME families in PreloadHTML",
"is_bot": false,
"headline": "Merge pull request #194 from linkdata/fix/182-preload-mime-family-slash",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T08:46:49Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "fdc3958de09fd90a80bf04b4721bf55a0bc6f42d",
"body": "Test_PreloadHTML computed its expected font preload link with\nstrings.HasPrefix(fontMime, \"font\"), a case-sensitive check without the\ntrailing slash. Since PreloadHTML now matches \"font/\" case-insensitively,\na platform returning \"FONT/woff2\" would emit as=\"font\" while the test\nexpected the bare preload form, failing spuriously. Mirror the production\nclassification so the expectation tracks the code on any MIME table.",
"is_bot": false,
"headline": "test: classify font family case-insensitively in expectation",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T08:43:18Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ba0964dfa323966d29413b0d1651c34dba3deb58",
"body": "assets.PreloadHTML classified MIME families with HasPrefix(mimetype,\n\"image\") and \"font\", so unrelated types such as \"imagery/*\" and\n\"fontastic/*\" were treated as images or fonts. That let non-image\nresources be emitted as favicons and non-font resources receive font\npreload metadata. Matching was a\n[…]\nike \"IMAGE/*\" or \"FONT/*\".\n\nMatch on the \"image/\" and \"font/\" prefixes case-insensitively so only\ngenuine image/* types qualify as favicons and only genuine font/* types\nreceive font preload handling.",
"is_bot": false,
"headline": "fix: require \"type/\" prefix when matching MIME families in PreloadHTML",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T08:37:13Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "eb2182a30c64c28bbf7124700329d4eca957b489",
"body": "fix: truncate inbound Set/Call data at first tab in wire.Parse",
"is_bot": false,
"headline": "Merge pull request #193 from linkdata/fix/181-set-call-truncate-at-tab",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T08:33:27Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "43c932d965a3b36b5605dfc67a7a7c4019d42e76",
"body": "wire.Parse documents that inbound Set and Call data is best-effort and\nends at the first tab, but it assigned the whole remainder after the Jid\nfield to Data, so a tab-separated suffix from an untrusted frame leaked\npast the documented boundary into Set/Call handling.\n\nCut the verbatim Set/Call fiel\n[…]\n so only the recoverable\nprefix survives, matching the parser contract. Non-Set/Call frames and\ntab-free Set/Call frames are unaffected, so existing Append/Parse round\ntrips are unchanged.\n\nFixes #181",
"is_bot": false,
"headline": "fix: truncate inbound Set/Call data at first tab in wire.Parse",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T08:27:02Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "7cc53f304846228d7d455a6e9680bd5ab30110b9",
"body": "fix: WsMsg.FillAlert panics on nil error (#180)",
"is_bot": false,
"headline": "Merge pull request #192 from linkdata/fix/180-fillalert-nil",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T08:23:20Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "5366996a0bacc5a465cde9c7e81c2690c49c574f",
"body": "FillAlert called err.Error() unconditionally, panicking on a nil error.\nTreat nil as an empty message so optional-error paths produce a\ndeterministic danger alert instead of crashing, and document the\nbehavior. Add a regression test covering nil alongside the existing\nescaping cases.\n\nFixes #180",
"is_bot": false,
"headline": "fix: handle nil error in WsMsg.FillAlert without panicking",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T07:52:27Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "577c7c6bf1cb66c2577598d435f50a84133f542a",
"body": "fix: terminate the Request on non-finite floats and non-reflexive UI values",
"is_bot": false,
"headline": "Merge pull request #191 from linkdata/fix/179-terminate-on-nonfinite",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T07:48:31Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "3b07ca728682aa9d8da8e9c47746fee7f30be88f",
"body": "…cile outputs\n\nA GetElementByJid == nil check is also satisfied by a created-then-deleted Element, so\nit did not prove the \"no child created\" guarantee. Because Jids are monotonic, both\nprevalidation tests now create a probe Element after the abort and assert it receives\nthe expected next Jid; a cre\n[…]\nile directly and asserts all four returned\noperation sets are empty, so UpdateContainer has nothing to queue — observing the\nreconciliation outputs rather than asserting the absence of ops indirectly.",
"is_bot": false,
"headline": "test: prove \"never created\" via monotonic-Jid probe and observe recon…",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T07:43:21Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "4cd18422522f3498a530236c4221caf8e3b023bb",
"body": "…dation tests\n\nTestContainerValidatesWholeSliceBeforeRender proved no rendering or commit but not the\nstated \"no child created\" guarantee. Assert via the Request registry that the Jid a\nvalid prefix child would have taken is unused, so NewElement was never reached.\n\nAdd TestContainerUpdateValidatesW\n[…]\nor the pool. Assert the existing child is not removed, the new\nvalid child is not created (its would-be Jid is unused), and u.contents is unchanged —\nwhich precludes any queued Remove/Append/Order op.",
"is_bot": false,
"headline": "test: prove no-child-created and no-partial-reconciliation in prevali…",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T07:34:38Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "5e6c0ea3d231979dc393ba0282ffe26652b67c76",
"body": "…ger tests\n\n - Add b.ResetTimer() after fixture creation in BenchmarkContainerValidateChildren so\n the one-time benchChildren allocation is excluded; -benchtime=1x now reports 0 B/op\n and 0 allocs instead of the setup cost.\n\n - Documentation precision: Element.JawsUpdate now says \"nil UI int\n[…]\ninerValidatesWholeSliceBeforeRender: a usable child\n preceding an unusable one is neither rendered nor committed, pinning that the\n pre-lock scan validates the whole slice before any child work.",
"is_bot": false,
"headline": "fix: address review round 5 — benchmark honesty, doc precision, stron…",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T07:22:38Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "efb4e92407ac8d13fd1d201cba91394bf57ee83e",
"body": "…ed benchmark\n\n - Clarify that only a nil interface is special. A typed nil (e.g. (*Widget)(nil)) is\n comparable and equal to itself, so NewErrUnusableUI reports it usable and the\n containers reconcile it normally; whether its methods tolerate a nil receiver is\n the UI implementation's res\n[…]\nred-recover scan is ~63% faster than per-child validation,\n but end-to-end container updates are statistically level with main: this is a\n no-regression change, not a claimed end-to-end speedup.",
"is_bot": false,
"headline": "fix: address review round 4 — typed-nil UI semantics, nil docs, focus…",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-21T07:00:57Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "ae55a3d4f1bb2783d674660c6cd9946e23618c88",
"body": "… regression\n\n - NewErrUnusableUI(nil) returned nil, so a nil UI passed container validation and\n later panicked in JawsRender. Reject nil explicitly (clear \"nil is not usable\"\n message), and make Element.JawsRender/JawsUpdate treat a nil UI as a no-op so a\n direct NewElement(nil) yields a\n[…]\ntest to assert the logger callback ran,\n so the check cannot pass vacuously. Update contract docs and the JAWS skill to\n describe the container-level guard and NewElement's debug-only assertion.",
"is_bot": false,
"headline": "fix: address review round 3 — nil UI, error wording, and append-heavy…",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T21:22:03Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "95f12619fadb61237ba2d714d2dca9845e513345",
"body": "…pecific error\n\n - ContainerHelper cancelled the Request while holding u.mu (reconcile validated\n each child inside the lock). Request.Cancel runs the user logger synchronously,\n which the locking contract forbids under a lock: a logger re-entering the\n container deadlocks. Validate the wh\n[…]\n update the tracked JAWS skill's comparability\n rules to require reflexivity and describe all-build cancellation.\n\n - Handle the previously blank-assigned render error in the container regression.",
"is_bot": false,
"headline": "fix: address review round 2 — cancel outside the container lock, UI-s…",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T20:35:18Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "3266b02c42185f308a6ee5e104b3b011356b3b1b",
"body": "…ch overflow\n\nFollow-up to review feedback on the non-finite/non-reflexive termination change:\n\n - Container reconcile hashed a wanted child UI before reaching the NewElement\n guard, so an interface-held slice/map (runtime-incomparable) panicked instead\n of cancelling. Validate each child wit\n[…]\n/JawsContains contract docs and the Number/Range docs to require\n reflexivity and describe all-build Request cancellation, replacing the stale\n debug-only-panic and blank/default-render wording.",
"is_bot": false,
"headline": "fix: address review — guard container reconcile, keep bind guard, cat…",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T20:01:04Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "7d2e698f8f92dda362fee6d57468b25d01afb070",
"body": "…values\n\nReplace the scattered NaN/±Inf handling with a uniform fail-fast policy. A\nnon-finite float64, or a UI value that is not equal to itself (issue #179),\nhas no valid rendering, wire, or map-key representation, so log the cause and\nterminate the offending Request instead of blanking, deduping,\n[…]\natForT keeps its\nfinite-out-of-range check (an infinity now falls out as ErrFloatOutOfRange) and\nno longer rejects finiteness itself. Removes the now-unused exported\nbind.ErrFloatNotFinite (breaking).",
"is_bot": false,
"headline": "fix: terminate the Request on non-finite floats and non-reflexive UI …",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T19:36:56Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "5d38e35eb27c14857013b905dd4e788fb91779ba",
"body": "fix(ui): render whole-page Handler dot as template data, not a tag",
"is_bot": false,
"headline": "Merge pull request #190 from linkdata/fix/178-page-dot-not-tag",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T18:54:30Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "5c99c8195206bb620a1341db56b885203a9a3018",
"body": "Render each request through a fresh per-request *pageTemplate so the UI stays\ncomparable as a map key regardless of the page dot. Ordinary html/template data\nsuch as a slice or map is not usable as a tag; a bare pageTemplate value\ncarrying it in its Dot any field is not comparable at runtime and tri\n[…]\niv).\n\nAdd a regression test rendering slice and map page dots (guarded with\ntag.NewErrNotComparable so it exercises the non-comparable path), and reword the\nhandler tests to describe current behavior.",
"is_bot": false,
"headline": "fix(ui): keep whole-page UI comparable for non-tag page dots",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T18:50:22Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "cdae354ad179377f407efc79965fd3bfacc00d97",
"body": "Add public-API regression tests for ui.Handler: a string dot and a struct\ndot render (the issue #178 repro), a missing template returns HTTP 500 with a\nlogged error, an execute error after output keeps the partial 200 body, a\npartial ui.Template still rejects a string dot, and a tag-valued page dot still\nrenders. Package coverage stays at 100% under -race.",
"is_bot": false,
"headline": "test(ui): cover Handler dot rendering and render-failure status",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T18:35:05Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "7e758f3d285adfeb59723af65c254e4a14f57ae7",
"body": "ui.Handler documents that each request renders the named template with dot\nexposed through With.Dot, with no tag-eligibility restriction. Whole-page\nrendering went through Template.render, which tag-expands the dot; a primitive\nvalue such as a string was rejected with ErrIllegalTagType and ServeHTTP\n[…]\nutput (for example a missing template) instead of a blank\n200. Once bytes are committed the partial body is left as-is, matching the\nbest-effort execution semantics documented on Template.\n\nFixes #178",
"is_bot": false,
"headline": "fix(ui): render whole-page Handler dot as template data, not a tag",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T18:35:05Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "56c7135b9638dd3ad854e3d084a449cd3a7e6506",
"body": "…ation\n\nFix numeric input adapter reconciliation",
"is_bot": false,
"headline": "Merge pull request #189 from linkdata/fix/176-numeric-input-reconcili…",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T18:15:01Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "d695f4e931e392664ac6243dde7396094c396481",
"body": "Add adapter subtests for negative int truncation and uint truncation, and\nparameterize the widget reconciliation test over both Number and Range.\n\nRefine the MakeSetterFloat64 doc: the converted value matches the underlying\nsetter's current value, not the setter itself.",
"is_bot": false,
"headline": "test(bind,ui): cover negative/uint truncation and range reconciliation",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T18:09:21Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ad05b591f9a18fb5a4fb2e7908247f5c1a06442d",
"body": null,
"is_bot": false,
"headline": "fix(bind): reconcile converted numeric inputs",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T17:30:17Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "f3d6d7d449a646d743a56798aac3c9e90b6aa812",
"body": "fix(ui): apply initial select getter",
"is_bot": false,
"headline": "Merge pull request #188 from linkdata/fix/177-select-initial-getter",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T17:17:27Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "3b0aad94adc97f0fd8ca6c3fe395e86fe78cd4a1",
"body": null,
"is_bot": false,
"headline": "test(ui): cover appended select initialization",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T16:56:46Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "bb1ce4425a1268ad4500d8e27ccebb9159d92f18",
"body": null,
"is_bot": false,
"headline": "fix(ui): apply initial select getter",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T16:42:11Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "18cdc998c407c9354d8c86054c367a037ad2b9d9",
"body": "…ract\n\ndocs: define BoolArray removal contract",
"is_bot": false,
"headline": "Merge pull request #187 from linkdata/docs/175-boolarray-removal-cont…",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T15:53:39Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "125c1344758fbab9ce4727b4b4c80e3967492d2c",
"body": null,
"is_bot": false,
"headline": "docs: define BoolArray removal contract",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T15:49:49Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "fab336bdc5f4228b62502be5f8842f914a8b209a",
"body": "Enforce MaxPendingRequestsPerIP as a hard cap",
"is_bot": false,
"headline": "Merge pull request #186 from linkdata/fix/174-strict-pending-cap",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T15:46:51Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "18b6b306ce500b4d059141c3f31f29e4245114df",
"body": "When every pending request for an IP is fresh, retire the least recently\nwritten one instead of the oldest-created one, and guard the eviction\nloop against spinning under jw.mu if retirement ever declines a victim.",
"is_bot": false,
"headline": "fix: evict least recently written pending request",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T15:43:53Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "075cb258589d4d8587b24db9e72e3f8c7597efc2",
"body": null,
"is_bot": false,
"headline": "fix: enforce pending request limit",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T15:24:11Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "31ce40bf34844760b102ae169f9c40d924cb29f8",
"body": "fix: isolate response header values",
"is_bot": false,
"headline": "Merge pull request #185 from linkdata/fix/173-response-header-ownership",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T15:16:28Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ad3effaed6ef1df0bede3e2aa2e3fd3bfb4468ea",
"body": null,
"is_bot": false,
"headline": "refactor: set content security policy header",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T15:12:44Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "3154b3716238134952c196091457da808a0a04f8",
"body": null,
"is_bot": false,
"headline": "fix: isolate response header values",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T15:05:59Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "96931906ac39621bc2908726a1907c965f16e704",
"body": "…er-lock\n\nAvoid calling response writers while holding the Jaws lock",
"is_bot": false,
"headline": "Merge pull request #184 from linkdata/fix/172-newsession-responsewrit…",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T14:55:36Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "5328b3555dd13a1dca88b8f425657570a49dc9d0",
"body": null,
"is_bot": false,
"headline": "docs: clarify NewSession lock boundary",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T14:51:57Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "6e26fb161e96fbd67ff61cbcf62486ac70fbca25",
"body": null,
"is_bot": false,
"headline": "fix: avoid NewSession writer deadlock",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T14:46:51Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "384702b967a4ab9db95f603968432eaf552c8b12",
"body": "Fix ConnectFn failure cleanup for non-reading peers",
"is_bot": false,
"headline": "Merge pull request #183 from linkdata/fix/171-connectfn-error-blocking",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T14:29:32Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "5b13b13863fd663962634b09c33c04260f5f3cbf",
"body": null,
"is_bot": false,
"headline": "fix: close websocket on ConnectFn failure",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T14:22:16Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "18d636e05532e9d5cc58e7040e660bdb729faeef",
"body": "Deliver broadcasts issued by ConnectFn",
"is_bot": false,
"headline": "Merge pull request #170 from linkdata/fix/161-connectfn-broadcasts",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T13:18:55Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "6249ed09870e9fcfe0e5aec653077fc02f52e9d8",
"body": null,
"is_bot": false,
"headline": "refactor: simplify ConnectFn subscription flow",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T13:11:20Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "a0f2a56a87065fa3f322139934b2e9e5f08cb635",
"body": "Signed-off-by: Johan Lindh <johan@linkdata.se>",
"is_bot": false,
"headline": "Fprint",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T13:04:34Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "2c6f8ad445385215f27e7031240644e762f45e42",
"body": null,
"is_bot": false,
"headline": "fix: deliver ConnectFn broadcasts",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T13:02:58Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "8d8826c5485f37d13f73a25398d287bacefad4be",
"body": "fix: render debug tags outside request lock",
"is_bot": false,
"headline": "Merge pull request #169 from linkdata/fix/162-debug-render-lock",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T12:36:42Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "be4748d0d6bf64643159dbf4b4c835a9b35c6549",
"body": null,
"is_bot": false,
"headline": "refactor: narrow debug tag snapshot helper",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T12:33:43Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "b0467fd005d670e3c3dba5dd1eccc155087df19b",
"body": null,
"is_bot": false,
"headline": "fix: render debug tags outside request lock",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T12:19:32Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "fbdda8dac4d01e75733f2cbbf4c02bfc99f23407",
"body": "fix(setup): root empty-prefix static assets",
"is_bot": false,
"headline": "Merge pull request #168 from linkdata/fix/163-setup-empty-prefix-assets",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T11:38:12Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "7f4ac2574b14d6b41b44d516e9e9fd5a47e73c7b",
"body": null,
"is_bot": false,
"headline": "docs(setup): define StaticServe name escaping",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T11:32:02Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "99961acdb9a42fbebd69f17a09ac25a2d57c1ee1",
"body": null,
"is_bot": false,
"headline": "fix(setup): root empty-prefix static assets",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T09:47:37Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "e23534b732e58cb9805b838c625df5de1bcea50e",
"body": "docs: define UI element multiplicity",
"is_bot": false,
"headline": "Merge pull request #167 from linkdata/docs/ui-element-multiplicity",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T09:19:51Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "a69aaa5151da3b9f089264c9764a662fbd26593f",
"body": null,
"is_bot": false,
"headline": "docs: address UI multiplicity review",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T09:16:05Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c5f0965c9bacddc52c74245abfa6dfa14ef1d540",
"body": null,
"is_bot": false,
"headline": "docs: define UI element multiplicity",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T09:01:00Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "bbc1657c36e8de488410b28a6066da1368d4d9ad",
"body": "ui: add transactional JsVar client validation",
"is_bot": false,
"headline": "Merge pull request #166 from linkdata/feat/jsvar-client-check",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T08:21:59Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "30e3f59bc9d9632d3c80fd0bb7ac3188a6ef2ff1",
"body": "Replace binding-local size accounting with an optional validation callback that observes the complete tentative state and changed jq path. Add JSONSizeCheck, atomic rollback, shared-state coverage, documentation, and retained benchmarks.",
"is_bot": false,
"headline": "ui: add transactional JsVar client checks",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-20T08:08:35Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "89f0e5ea8487b54cd52f6ea39199b9cd7a8ed233",
"body": "Signed-off-by: Johan Lindh <johan@linkdata.se>",
"is_bot": false,
"headline": "jq v0.2.0",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-19T23:09:35Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "70886189bb437e2a647a60e7c19768ca1f9e7856",
"body": "… test\n\nTestJawsJS_ConnectsAfterDeferredAssets simulated deferred assets with\nwindow.jawstreeInit and window.Treeview globals, but jaws.js connects on the\nDOMContentLoaded/load events and never inspects any adapter global, so the\nconnectedAfterAssets check only re-verified the snippet's own setup ordering.\nDrop the globals and that assertion, keeping the real checks: jaws connects\nexactly once, only after the ready event, idempotent across duplicate events.",
"is_bot": false,
"headline": "test(assets): drop tautological jawstree fixture from deferred-assets…",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-19T21:14:41Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "717e5cd2fefe2749408c2b4ab574919f140ab21d",
"body": "Move the jawstree treeview widget to the standalone\ngithub.com/linkdata/jawstree module and repository.\n\n- Remove the jawstree package and its embedded Quercus.js assets.\n- Drop the jsdom install step from CI: only jawstree loaded pages under\n jsdom; the remaining node-driven JS tests (lib/assets) need only node.\n- Narrow the 386 test leg to lib/bind.\n- Repoint the jawstree cross-references in errors.go, lib/ui/jsvar.go,\n and SECURITY.md to the external module path.",
"is_bot": false,
"headline": "Extract jawstree into its own module",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-19T21:00:38Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "68a46a06f7ebdd69a50151ccf05104714ecfa5f2",
"body": "Bring jawstree to 100% statement coverage under -race by exercising the\nremaining untested branches:\n\n- applyClientAbsolute: invalid-index and disabled-node rejections.\n- applyClientDelta single-select: disabled-add reject, remove-clears-\n selection, remove-unselected no-op, and invalid-remove-inde\n[…]\nx reject.\n- JawsInput: bitmap (\"b\") input, empty-payload, and malformed-JSON\n reject/resync cases.\n- sparsePayloadLocked: multi-index wire form.\n- Node.HasNames: root vs non-root empty-path matching.",
"is_bot": false,
"headline": "test(jawstree): cover selection error and edge branches",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-19T20:28:01Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "1eeac3165d904e8f73a44c0af0e89824e0f0862b",
"body": "click: build Click.Name in linear time when parsing",
"is_bot": false,
"headline": "Merge pull request #157 from linkdata/fix/click-name-linear",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-19T19:38:27Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c858c1a4fccf4236ccd623941c54f1c2139986bf",
"body": null,
"is_bot": false,
"headline": "Merge branch 'main' into fix/click-name-linear",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-19T19:35:31Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "64885208295c05de2324d33efa7b4bf94455f386",
"body": "…ftover\n\nui: skip out-of-band-deleted leftovers in container reconcile",
"is_bot": false,
"headline": "Merge pull request #158 from linkdata/fix/container-remove-deleted-le…",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-19T19:32:05Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "29866a87c2f9d90a5a330fdde8052d837aaa9fb8",
"body": "parseClickData accumulated the click name with clk.Name += \" \" + field\nonce per whitespace-separated token past the fourth. Go strings are\nimmutable, so each += reallocated and copied the whole accumulated name,\nmaking the parse O(n^2) in the token count. The click frame is untrusted\nbrowser input b\n[…]\nhmarks (b.Loop, Apple M5 Max):\n OneToken 0 allocs/op (unchanged fast path)\n TwoTokens 1 alloc/op (unchanged)\n LongName, 8000 tokens 5380us/8003 allocs -> 43us/17 allocs",
"is_bot": false,
"headline": "click: build Click.Name in linear time when parsing",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-19T19:27:28Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "39c18f8dc0e0bc42e69da00fbb9c7448cb146f55",
"body": "…deleted-leftover",
"is_bot": false,
"headline": "Merge remote-tracking branch 'origin/main' into fix/container-remove-…",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-19T19:20:57Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "a9a8c6bd527d4ef221bdacf21e7e581660d460e4",
"body": null,
"is_bot": false,
"headline": "test(ui): strengthen deleted-leftover regression",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-19T19:18:44Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "22c7a4c36c3c6f48ba9440b91e1e376c44298433",
"body": "request: tolerate a nil *Element in DeleteElement",
"is_bot": false,
"headline": "Merge pull request #156 from linkdata/fix/deleteelement-nil-guard",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-19T19:13:51Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "2b0c5558896975f18bde33677dfd3efa39a95777",
"body": "ContainerHelper.reconcile's reuse loop already discards Elements deleted\nout-of-band (a what.Delete broadcast or browser what.Remove) so the\ncontainer self-heals a still-wanted child. But the leftover sweep that\nbuilds toRemove appended every remaining pooled Element unconditionally,\nso a child that\n[…]\nc-API use;\nreconcile's own doc anticipates the out-of-band deletion.\n\nMirror the reuse loop's !Deleted() filter into the leftover sweep so a\ndeleted, already-unregistered Element never reaches Remove.",
"is_bot": false,
"headline": "ui: skip out-of-band-deleted leftovers in container reconcile",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-19T19:05:01Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "317f8beb443f142c5b97a2d1297f816ab1827648",
"body": "deleteElementLocked read elem.Request before any nil check, so\nRequest.DeleteElement(nil) dereferenced a nil *Element and panicked.\nElement embeds *Request as its first field, so the field read faults on a\nnil receiver.\n\nEvery sibling *Element-accepting method already tolerates nil (Tag,\nTagExpanded\n[…]\nor\nan unknown Jid, so the natural rq.DeleteElement(rq.GetElementByJid(id))\ncomposed two documented behaviors into a panic. Guard elem != nil to match\nthe rest of the family and document the nil no-op.",
"is_bot": false,
"headline": "request: tolerate a nil *Element in DeleteElement",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-19T19:00:07Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "dbc26c41a40bd33dc9611980579181136b287666",
"body": "jawstree: cover fresh-client cascade selection regression",
"is_bot": false,
"headline": "Merge pull request #155 from linkdata/fix/134-jawstree-cascade-init",
"author_name": "Johan Lindh",
"author_login": "linkdata",
"committed_at": "2026-07-19T18:06:25Z",
"body_truncated": false,
"is_coding_agent": false
}
],
"releases_count": 100,
"commits_last_year": 1209,
"latest_release_at": "2026-06-23T07:37:08Z",
"latest_release_tag": "v0.600.0",
"releases_from_tags": false,
"days_since_last_push": 3,
"active_weeks_last_year": 28,
"days_since_latest_release": 34,
"mean_days_between_releases": 14.5
},
"community": {
"has_readme": true,
"has_license": true,
"has_description": true,
"has_contributing": false,
"health_percentage": 57,
"has_issue_template": false,
"has_code_of_conduct": false,
"has_pull_request_template": false
},
"ecosystem": {
"packages": [
{
"name": "github.com/linkdata/jaws",
"exists": true,
"license": null,
"keywords": [],
"ecosystem": "go",
"matches_repo": true,
"registry_url": "https://pkg.go.dev/github.com/linkdata/jaws",
"is_deprecated": false,
"latest_version": "v0.601.0",
"repository_url": "https://github.com/linkdata/jaws",
"versions_count": 292,
"total_downloads": null,
"dependents_count": null,
"deprecation_note": null,
"maintainers_count": null,
"monthly_downloads": null,
"first_published_at": null,
"latest_published_at": "2026-07-19T21:00:38Z",
"latest_version_yanked": null,
"days_since_latest_publish": 7
}
]
},
"popularity": {
"forks": 2,
"stars": 6,
"watchers": 1,
"fork_history": {
"days": [
{
"date": "2022-08-22",
"count": 1
},
{
"date": "2026-07-18",
"count": 1
}
],
"complete": true,
"collected": 2,
"total_forks": 2
},
"star_history": null,
"open_issues_and_prs": 4
},
"ai_readiness": {
"has_nix": false,
"example_dirs": [
"examples"
],
"has_llms_txt": false,
"has_dockerfile": false,
"has_mcp_signal": false,
"bootstrap_files": [],
"api_schema_files": [],
"has_devcontainer": false,
"typecheck_configs": [],
"toolchain_manifests": [
"go.mod"
],
"largest_source_bytes": 110794,
"source_files_sampled": 193,
"oversized_source_files": 3,
"agent_instruction_files": [],
"agent_instruction_max_bytes": null
},
"dependencies": {
"manifests": [
"go.mod"
],
"advisories": {
"error": null,
"scope": "repository_graph",
"source": "osv",
"findings": [],
"collected": true,
"malicious": [],
"truncated": false,
"by_severity": {},
"advisory_count": 0,
"affected_count": 0,
"assessed_count": 6,
"malicious_count": 0,
"assessed_package": null,
"unassessed_count": 0,
"direct_affected_count": 0
},
"ecosystems": [
"go"
],
"dependencies": [
{
"name": "github.com/coder/websocket",
"manifest": "go.mod",
"ecosystem": "go",
"version_constraint": "v1.8.15"
},
{
"name": "github.com/linkdata/deadlock",
"manifest": "go.mod",
"ecosystem": "go",
"version_constraint": "v0.5.5"
},
{
"name": "github.com/linkdata/jq",
"manifest": "go.mod",
"ecosystem": "go",
"version_constraint": "v0.2.0"
},
{
"name": "github.com/linkdata/secureheaders",
"manifest": "go.mod",
"ecosystem": "go",
"version_constraint": "v1.1.1"
},
{
"name": "github.com/linkdata/staticserve",
"manifest": "go.mod",
"ecosystem": "go",
"version_constraint": "v1.1.8"
}
],
"all_dependencies": {
"error": null,
"source": "github-sbom",
"packages": [
{
"name": "github.com/coder/websocket",
"direct": true,
"version": "v1.8.15",
"ecosystem": "go"
},
{
"name": "github.com/linkdata/deadlock",
"direct": true,
"version": "v0.5.5",
"ecosystem": "go"
},
{
"name": "github.com/linkdata/jq",
"direct": true,
"version": "v0.2.0",
"ecosystem": "go"
},
{
"name": "github.com/linkdata/secureheaders",
"direct": true,
"version": "v1.1.1",
"ecosystem": "go"
},
{
"name": "github.com/linkdata/staticserve",
"direct": true,
"version": "v1.1.8",
"ecosystem": "go"
},
{
"name": "github.com/petermattis/goid",
"direct": false,
"version": "v0.0.0-20260330135022-df67b199bc81",
"ecosystem": "go"
}
],
"collected": true,
"truncated": false,
"total_count": 6,
"direct_count": 5,
"indirect_count": 1
}
},
"maintainership": {
"issues": {
"open_prs": 0,
"merged_prs": 150,
"open_issues": 4,
"closed_ratio": 0.932,
"closed_issues": 55,
"closed_unmerged_prs": 11
},
"bus_factor": 1,
"bot_contributors": 1,
"top_contributors": [
{
"type": "User",
"login": "linkdata",
"commits": 2021,
"avatar_url": "https://avatars.githubusercontent.com/u/2185977?v=4"
},
{
"type": "User",
"login": "elri",
"commits": 20,
"avatar_url": "https://avatars.githubusercontent.com/u/14096948?v=4"
}
],
"contributors_sampled": 2,
"top_contributor_share": 0.99
},
"quality_signals": {
"has_ci": true,
"has_tests": true,
"ci_workflows": [
"build.yml",
"codeql.yml",
"scorecard.yml"
],
"has_docs_dir": false,
"linter_configs": [
".golangci.yml"
],
"has_editorconfig": false,
"has_linter_config": true,
"has_precommit_config": false
},
"security_signals": {
"lockfiles": [
"go.sum"
],
"scorecard": {
"checks": [
{
"name": "Binary-Artifacts",
"score": 10,
"reason": "no binaries found in the repo",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#binary-artifacts"
},
{
"name": "Branch-Protection",
"score": null,
"reason": "internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#branch-protection"
},
{
"name": "CI-Tests",
"score": 10,
"reason": "10 out of 10 merged PRs checked by a CI test -- score normalized to 10",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#ci-tests"
},
{
"name": "CII-Best-Practices",
"score": 0,
"reason": "no effort to earn an OpenSSF best practices badge detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#cii-best-practices"
},
{
"name": "Code-Review",
"score": 0,
"reason": "Found 0/12 approved changesets -- score normalized to 0",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
},
{
"name": "Contributors",
"score": 0,
"reason": "project has 0 contributing companies or organizations -- score normalized to 0",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#contributors"
},
{
"name": "Dangerous-Workflow",
"score": 10,
"reason": "no dangerous workflow patterns detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dangerous-workflow"
},
{
"name": "Dependency-Update-Tool",
"score": 10,
"reason": "update tool detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dependency-update-tool"
},
{
"name": "Fuzzing",
"score": 10,
"reason": "project is fuzzed",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#fuzzing"
},
{
"name": "License",
"score": 10,
"reason": "license file detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#license"
},
{
"name": "Maintained",
"score": 10,
"reason": "30 commit(s) and 30 issue activity found in the last 90 days -- score normalized to 10",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
},
{
"name": "Packaging",
"score": null,
"reason": "packaging workflow not detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
},
{
"name": "Pinned-Dependencies",
"score": 10,
"reason": "all dependencies are pinned",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#pinned-dependencies"
},
{
"name": "SAST",
"score": 10,
"reason": "SAST tool is run on all commits",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#sast"
},
{
"name": "Security-Policy",
"score": 10,
"reason": "security policy file detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#security-policy"
},
{
"name": "Signed-Releases",
"score": null,
"reason": "no releases found",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#signed-releases"
},
{
"name": "Token-Permissions",
"score": 10,
"reason": "GitHub workflow tokens follow principle of least privilege",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#token-permissions"
},
{
"name": "Vulnerabilities",
"score": 10,
"reason": "0 existing vulnerabilities detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
}
],
"commit": "a71480a083e0efa28f6bcb5db716d93699dca694",
"ran_at": "2026-07-27T14:07:23Z",
"aggregate_score": 8.5,
"scorecard_version": "v5.5.0"
},
"has_codeql_workflow": true,
"has_security_policy": true,
"has_dependabot_config": true
},
"contribution_flow": {
"collected": true,
"ci_last_run_at": "2026-07-27T04:24:08Z",
"oldest_open_prs": [],
"last_merged_pr_at": "2026-07-23T17:58:39Z",
"ci_last_conclusion": "SUCCESS",
"oldest_open_issues": [
{
"number": 216,
"created_at": "2026-07-22T20:40:15Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 217,
"created_at": "2026-07-22T20:40:16Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 218,
"created_at": "2026-07-22T20:40:16Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 219,
"created_at": "2026-07-22T20:40:17Z",
"last_comment_at": null,
"last_comment_author": null
}
]
}
},
"config": {
"disabled_metrics": [],
"disabled_categories": [],
"disabled_components": {}
},
"source": {
"url": "https://github.com/linkdata/jaws",
"host": "github.com",
"name": "jaws",
"owner": "linkdata"
},
"metrics": {
"overall": {
"key": "overall",
"band": "moderate",
"name": "Overall health",
"note": null,
"notes": [],
"value": 67,
"inputs": {
"security": 88,
"vitality": 90,
"community": 29,
"governance": 56,
"engineering": 74
},
"components": []
},
"categories": [
{
"key": "vitality",
"band": "excellent",
"name": "Vitality",
"value": 90,
"weight": 0.22,
"metrics": [
{
"key": "development_activity",
"band": "good",
"name": "Development activity",
"note": null,
"notes": [],
"value": 83,
"inputs": {
"commits_last_year": 1209,
"human_commit_share": 1,
"days_since_last_push": 3,
"active_weeks_last_year": 28
},
"components": [
{
"key": "push_recency",
"name": "Push recency",
"detail": "last push 3 days ago",
"points": 36,
"status": "met",
"details": [
{
"code": "push_recency",
"params": {
"days": 3
}
}
],
"max_points": 36
},
{
"key": "commit_cadence",
"name": "Commit cadence",
"detail": "28/52 weeks with commits",
"points": 19.4,
"status": "partial",
"details": [
{
"code": "commit_cadence_weeks",
"params": {
"weeks": 28
}
}
],
"max_points": 36
},
{
"key": "commit_volume",
"name": "Commit volume",
"detail": "1209 commits in the last year",
"points": 18,
"status": "met",
"details": [
{
"code": "commits_last_year",
"params": {
"count": 1209
}
}
],
"max_points": 18
},
{
"key": "openssf_scorecard_maintained",
"name": "OpenSSF Scorecard: Maintained",
"detail": "30 commit(s) and 30 issue activity found in the last 90 days -- score normalized to 10",
"points": 10,
"status": "met",
"details": [],
"max_points": 10
}
]
},
{
"key": "release_discipline",
"band": "excellent",
"name": "Release discipline",
"note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Signed-Releases. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"openssf_scorecard_signed_releases"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 100,
"inputs": {
"releases_count": 100,
"latest_release_tag": "v0.600.0",
"releases_from_tags": false,
"days_since_latest_release": 34,
"mean_days_between_releases": 14.5
},
"components": [
{
"key": "ships_releases",
"name": "Ships releases",
"detail": "100 releases published",
"points": 27,
"status": "met",
"details": [
{
"code": "releases_published",
"params": {
"count": 100
}
}
],
"max_points": 27
},
{
"key": "release_recency",
"name": "Release recency",
"detail": "latest release 34 days ago",
"points": 36,
"status": "met",
"details": [
{
"code": "release_recency",
"params": {
"days": 34
}
}
],
"max_points": 36
},
{
"key": "release_cadence",
"name": "Release cadence",
"detail": "a release every ~14.5 days",
"points": 27,
"status": "met",
"details": [
{
"code": "release_cadence",
"params": {
"gap": 14.5
}
}
],
"max_points": 27
},
{
"key": "openssf_scorecard_signed_releases",
"name": "OpenSSF Scorecard: Signed-Releases",
"detail": "no releases found",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 10
}
]
},
{
"key": "abandonment",
"band": "excellent",
"name": "Abandonment",
"note": null,
"notes": [],
"value": 100,
"inputs": {
"cap": null,
"state": "maintained",
"guards": [],
"signals": [],
"red_flag": false,
"multiplier_pct": 100,
"declared_reason": null,
"unverified_reason": null,
"unanswered_open_prs": null,
"unanswered_open_issues": null,
"days_since_last_merged_pr": null,
"days_since_last_human_commit": 3,
"days_since_last_human_commit_is_floor": false
},
"components": [
{
"key": "project_is_still_maintained",
"name": "Project is still maintained",
"detail": "last human commit 3 days ago",
"points": 100,
"status": "met",
"details": [
{
"code": "abandonment_maintained",
"params": {
"days": 3
}
}
],
"max_points": 100
}
]
}
],
"description": "Is the project alive — is code being written and are releases shipping?"
},
{
"key": "community",
"band": "critical",
"name": "Community & Adoption",
"value": 29,
"weight": 0.18,
"metrics": [
{
"key": "popularity",
"band": "critical",
"name": "Popularity & adoption",
"note": null,
"notes": [],
"value": 11,
"inputs": {
"forks": 2,
"stars": 6,
"watchers": 1,
"growth_state": "unverified",
"growth_factor_pct": 100,
"growth_unverified_reason": "no_history"
},
"components": [
{
"key": "stars",
"name": "Stars",
"detail": "6 stars",
"points": 11.3,
"status": "partial",
"details": [
{
"code": "stars",
"params": {
"count": 6
}
}
],
"max_points": 60
},
{
"key": "forks",
"name": "Forks",
"detail": "2 forks",
"points": 0,
"status": "missed",
"details": [
{
"code": "forks",
"params": {
"count": 2
}
}
],
"max_points": 25
},
{
"key": "watchers",
"name": "Watchers",
"detail": "1 watchers",
"points": 0,
"status": "missed",
"details": [
{
"code": "watchers",
"params": {
"count": 1
}
}
],
"max_points": 15
}
]
},
{
"key": "community_health",
"band": "moderate",
"name": "Community health",
"note": null,
"notes": [],
"value": 50,
"inputs": {
"has_readme": true,
"has_license": true,
"has_contributing": false,
"has_issue_template": false,
"has_code_of_conduct": false,
"has_pull_request_template": false
},
"components": [
{
"key": "readme",
"name": "README",
"detail": null,
"points": 22.5,
"status": "met",
"details": [],
"max_points": 22.5
},
{
"key": "license",
"name": "License",
"detail": "recognized license (MIT)",
"points": 22.5,
"status": "met",
"details": [
{
"code": "license_standard",
"params": {}
},
{
"code": "license_spdx",
"params": {
"spdx": "MIT"
}
}
],
"max_points": 22.5
},
{
"key": "contributing_guide",
"name": "CONTRIBUTING guide",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 18
},
{
"key": "code_of_conduct",
"name": "Code of conduct",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 13.5
},
{
"key": "issue_template",
"name": "Issue template",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.2
},
{
"key": "pr_template",
"name": "PR template",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 6.3
}
]
}
],
"description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
},
{
"key": "governance",
"band": "moderate",
"name": "Sustainability & Governance",
"value": 56,
"weight": 0.24,
"metrics": [
{
"key": "maintainer_resilience",
"band": "critical",
"name": "Maintainer resilience (bus factor)",
"note": null,
"notes": [],
"value": 12,
"inputs": {
"bus_factor": 1,
"contributors_sampled": 2,
"top_contributor_share": 0.99
},
"components": [
{
"key": "bus_factor",
"name": "Bus factor",
"detail": "1 contributor(s) cover half of all commits",
"points": 9,
"status": "partial",
"details": [
{
"code": "bus_factor",
"params": {
"count": 1
}
}
],
"max_points": 54
},
{
"key": "commit_distribution",
"name": "Commit distribution",
"detail": "top contributor authored 99% of commits",
"points": 0.2,
"status": "partial",
"details": [
{
"code": "top_contributor_share",
"params": {
"share": 99
}
}
],
"max_points": 22.5
},
{
"key": "contributor_breadth",
"name": "Contributor breadth",
"detail": "2 contributors",
"points": 2.7,
"status": "partial",
"details": [
{
"code": "contributors_sampled",
"params": {
"count": 2
}
}
],
"max_points": 13.5
},
{
"key": "openssf_scorecard_contributors",
"name": "OpenSSF Scorecard: Contributors",
"detail": "project has 0 contributing companies or organizations -- score normalized to 0",
"points": 0,
"status": "missed",
"details": [],
"max_points": 10
}
]
},
{
"key": "responsiveness",
"band": "good",
"name": "Issue & PR responsiveness",
"note": null,
"notes": [],
"value": 79,
"inputs": {
"merged_prs": 150,
"open_issues": 4,
"closed_issues": 55,
"issue_closed_ratio": 0.932,
"closed_unmerged_prs": 11
},
"components": [
{
"key": "issue_resolution",
"name": "Issue resolution",
"detail": "93% of issues closed",
"points": 43.6,
"status": "partial",
"details": [
{
"code": "issues_closed_share",
"params": {
"share": 93
}
}
],
"max_points": 46.75
},
{
"key": "pr_acceptance",
"name": "PR acceptance",
"detail": "150/161 decided PRs merged",
"points": 35.6,
"status": "partial",
"details": [
{
"code": "decided_prs_merged",
"params": {
"merged": 150,
"decided": 161
}
}
],
"max_points": 38.25
},
{
"key": "openssf_scorecard_code_review",
"name": "OpenSSF Scorecard: Code-Review",
"detail": "Found 0/12 approved changesets -- score normalized to 0",
"points": 0,
"status": "missed",
"details": [],
"max_points": 15
}
]
},
{
"key": "stewardship",
"band": "moderate",
"name": "Ownership & stewardship",
"note": "Excluded from scoring (no data or not applicable): Verified domain. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"verified_domain"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 52,
"inputs": {
"followers": 10,
"owner_type": "User",
"is_verified": null,
"owner_login": "linkdata",
"public_repos": 44,
"account_age_days": 5088
},
"components": [
{
"key": "ownership_backing",
"name": "Ownership backing",
"detail": "personal (user) account",
"points": 10,
"status": "partial",
"details": [
{
"code": "owner_personal",
"params": {}
}
],
"max_points": 30
},
{
"key": "verified_domain",
"name": "Verified domain",
"detail": "not applicable to user accounts",
"points": 0,
"status": "excluded",
"details": [
{
"code": "not_applicable_to_user_accounts",
"params": {}
}
],
"max_points": 20
},
{
"key": "owner_reach",
"name": "Owner reach",
"detail": "10 followers of linkdata",
"points": 7.5,
"status": "partial",
"details": [
{
"code": "owner_followers",
"params": {
"count": 10,
"login": "linkdata"
}
}
],
"max_points": 25
},
{
"key": "track_record",
"name": "Track record",
"detail": "44 public repos, account ~13 yr old",
"points": 24,
"status": "partial",
"details": [
{
"code": "public_repos",
"params": {
"count": 44
}
},
{
"code": "account_age_years",
"params": {
"years": 13
}
}
],
"max_points": 25
}
]
},
{
"key": "package_maintenance",
"band": "excellent",
"name": "Package maintenance",
"note": null,
"notes": [],
"value": 100,
"inputs": {
"packages": [
"github.com/linkdata/jaws"
],
"ecosystems": "go",
"any_deprecated": false,
"min_days_since_publish": 7
},
"components": [
{
"key": "published_resolvable",
"name": "Published & resolvable",
"detail": "1 package(s) on go",
"points": 25,
"status": "met",
"details": [
{
"code": "packages_published",
"params": {
"count": 1,
"ecosystems": "go"
}
}
],
"max_points": 25
},
{
"key": "publish_recency",
"name": "Publish recency",
"detail": "latest publish 7 days ago",
"points": 35,
"status": "met",
"details": [
{
"code": "publish_recency",
"params": {
"days": 7
}
}
],
"max_points": 35
},
{
"key": "version_history",
"name": "Version history",
"detail": "292 published versions",
"points": 20,
"status": "met",
"details": [
{
"code": "published_versions",
"params": {
"count": 292
}
}
],
"max_points": 20
},
{
"key": "not_deprecated",
"name": "Not deprecated",
"detail": "active, not deprecated or yanked",
"points": 20,
"status": "met",
"details": [
{
"code": "package_not_deprecated",
"params": {}
}
],
"max_points": 20
}
]
}
],
"description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
},
{
"key": "engineering",
"band": "good",
"name": "Engineering Quality",
"value": 74,
"weight": 0.2,
"metrics": [
{
"key": "engineering_practices",
"band": "good",
"name": "Engineering practices",
"note": null,
"notes": [],
"value": 84,
"inputs": {
"has_ci": true,
"has_tests": true,
"has_editorconfig": false,
"has_linter_config": true,
"has_precommit_config": false
},
"components": [
{
"key": "ci_workflows",
"name": "CI workflows",
"detail": "3 workflow(s)",
"points": 24,
"status": "met",
"details": [
{
"code": "ci_workflows",
"params": {
"count": 3
}
}
],
"max_points": 24
},
{
"key": "tests_present",
"name": "Tests present",
"detail": null,
"points": 24,
"status": "met",
"details": [],
"max_points": 24
},
{
"key": "linter_config",
"name": "Linter config",
"detail": ".golangci.yml",
"points": 16,
"status": "met",
"details": [
{
"code": "file_list",
"params": {
"files": ".golangci.yml"
}
}
],
"max_points": 16
},
{
"key": "pre_commit_hooks",
"name": "Pre-commit hooks",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 9.6
},
{
"key": "editorconfig",
"name": ".editorconfig",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 6.4
},
{
"key": "openssf_scorecard_ci_tests",
"name": "OpenSSF Scorecard: CI-Tests",
"detail": "10 out of 10 merged PRs checked by a CI test -- score normalized to 10",
"points": 20,
"status": "met",
"details": [],
"max_points": 20
}
]
},
{
"key": "documentation",
"band": "moderate",
"name": "Documentation",
"note": null,
"notes": [],
"value": 60,
"inputs": {
"topics": [
"dynamic-web",
"dynamic-webpages",
"go",
"golang",
"websocket",
"websockets",
"echo-framework",
"react",
"ui",
"frontend",
"web"
],
"has_wiki": true,
"homepage": null,
"has_readme": true,
"has_docs_dir": false,
"has_description": true
},
"components": [
{
"key": "readme",
"name": "README",
"detail": null,
"points": 30,
"status": "met",
"details": [],
"max_points": 30
},
{
"key": "documentation_directory",
"name": "Documentation directory",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 25
},
{
"key": "documentation_homepage_site",
"name": "Documentation / homepage site",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 15
},
{
"key": "repository_description",
"name": "Repository description",
"detail": null,
"points": 10,
"status": "met",
"details": [],
"max_points": 10
},
{
"key": "topics",
"name": "Topics",
"detail": "11 topics",
"points": 10,
"status": "met",
"details": [
{
"code": "topics_count",
"params": {
"count": 11
}
}
],
"max_points": 10
},
{
"key": "wiki",
"name": "Wiki",
"detail": null,
"points": 10,
"status": "met",
"details": [],
"max_points": 10
}
]
}
],
"description": "Are baseline engineering and documentation practices in place?"
},
{
"key": "security",
"band": "excellent",
"name": "Security",
"value": 88,
"weight": 0.16,
"metrics": [
{
"key": "security_posture",
"band": "excellent",
"name": "Security posture",
"note": "Excluded from scoring (no data or not applicable): Branch-Protection, Packaging, Signed-Releases. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"branch_protection",
"packaging",
"signed_releases"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 85,
"inputs": {
"source": "openssf_scorecard",
"checks_evaluated": 15,
"scorecard_version": "v5.5.0",
"checks_inconclusive": 3,
"scorecard_aggregate": 8.5
},
"components": [
{
"key": "binary_artifacts",
"name": "Binary-Artifacts",
"detail": "no binaries found in the repo",
"points": 7.5,
"status": "met",
"details": [],
"max_points": 7.5
},
{
"key": "branch_protection",
"name": "Branch-Protection",
"detail": "internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 7.5
},
{
"key": "ci_tests",
"name": "CI-Tests",
"detail": "10 out of 10 merged PRs checked by a CI test -- score normalized to 10",
"points": 2.5,
"status": "met",
"details": [],
"max_points": 2.5
},
{
"key": "cii_best_practices",
"name": "CII-Best-Practices",
"detail": "no effort to earn an OpenSSF best practices badge detected",
"points": 0,
"status": "missed",
"details": [],
"max_points": 2.5
},
{
"key": "code_review",
"name": "Code-Review",
"detail": "Found 0/12 approved changesets -- score normalized to 0",
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.5
},
{
"key": "contributors",
"name": "Contributors",
"detail": "project has 0 contributing companies or organizations -- score normalized to 0",
"points": 0,
"status": "missed",
"details": [],
"max_points": 2.5
},
{
"key": "dangerous_workflow",
"name": "Dangerous-Workflow",
"detail": "no dangerous workflow patterns detected",
"points": 10,
"status": "met",
"details": [],
"max_points": 10
},
{
"key": "dependency_update_tool",
"name": "Dependency-Update-Tool",
"detail": "update tool detected",
"points": 7.5,
"status": "met",
"details": [],
"max_points": 7.5
},
{
"key": "fuzzing",
"name": "Fuzzing",
"detail": "project is fuzzed",
"points": 5,
"status": "met",
"details": [],
"max_points": 5
},
{
"key": "license",
"name": "License",
"detail": "license file detected",
"points": 2.5,
"status": "met",
"details": [],
"max_points": 2.5
},
{
"key": "maintained",
"name": "Maintained",
"detail": "30 commit(s) and 30 issue activity found in the last 90 days -- score normalized to 10",
"points": 7.5,
"status": "met",
"details": [],
"max_points": 7.5
},
{
"key": "packaging",
"name": "Packaging",
"detail": "packaging workflow not detected",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 5
},
{
"key": "pinned_dependencies",
"name": "Pinned-Dependencies",
"detail": "all dependencies are pinned",
"points": 5,
"status": "met",
"details": [],
"max_points": 5
},
{
"key": "sast",
"name": "SAST",
"detail": "SAST tool is run on all commits",
"points": 5,
"status": "met",
"details": [],
"max_points": 5
},
{
"key": "security_policy",
"name": "Security-Policy",
"detail": "security policy file detected",
"points": 5,
"status": "met",
"details": [],
"max_points": 5
},
{
"key": "signed_releases",
"name": "Signed-Releases",
"detail": "no releases found",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 7.5
},
{
"key": "token_permissions",
"name": "Token-Permissions",
"detail": "GitHub workflow tokens follow principle of least privilege",
"points": 7.5,
"status": "met",
"details": [],
"max_points": 7.5
},
{
"key": "vulnerabilities",
"name": "Vulnerabilities",
"detail": "0 existing vulnerabilities detected",
"points": 7.5,
"status": "met",
"details": [],
"max_points": 7.5
}
]
},
{
"key": "dependency_advisories",
"band": "excellent",
"name": "Dependency advisories",
"note": "Excluded from scoring (no data or not applicable): Indirect dependencies free of known advisories, No advisories left outstanding. Remaining weights renormalized. Matched 6 resolved dependencies against OSV. This repository publishes no package the index resolves, so the repository dependency graph was assessed instead. That graph mixes development and test pins with shipped dependencies, so only the declared runtime dependencies are scored; transitive findings are reported as context and excluded from the score. Reachability is not analyzed.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"indirect_dependencies_free_of_known_advisories",
"no_advisories_left_outstanding"
]
}
},
{
"code": "weights_renormalized",
"params": {}
},
{
"code": "advisories_scope_repository",
"params": {
"assessed": 6
}
},
{
"code": "advisories_repo_graph_caveat",
"params": {}
},
{
"code": "advisories_reachability",
"params": {}
}
],
"value": 100,
"inputs": {
"source": "osv",
"advisories": 0,
"affected_packages": 0,
"assessed_packages": 6,
"unassessed_packages": 0,
"affected_by_severity": "none",
"direct_affected_packages": 0
},
"components": [
{
"key": "direct_dependencies_free_of_known_advisories",
"name": "Direct dependencies free of known advisories",
"detail": "no direct dependency carries a known advisory",
"points": 35,
"status": "met",
"details": [
{
"code": "no_direct_advisories",
"params": {}
}
],
"max_points": 35
},
{
"key": "indirect_dependencies_free_of_known_advisories",
"name": "Indirect dependencies free of known advisories",
"detail": "transitive set not separable from development and test dependencies in this scope",
"points": 0,
"status": "excluded",
"details": [
{
"code": "advisories_scope_not_separable",
"params": {}
}
],
"max_points": 25
},
{
"key": "no_advisories_left_outstanding",
"name": "No advisories left outstanding",
"detail": "no advisory carries a publication date",
"points": 0,
"status": "excluded",
"details": [
{
"code": "advisories_no_publication_date",
"params": {}
}
],
"max_points": 40
}
]
},
{
"key": "malicious_dependencies",
"band": "excellent",
"name": "Malicious dependencies",
"note": null,
"notes": [],
"value": 100,
"inputs": {
"source": "osv",
"meaning": "reported as a malicious package by the OpenSSF corpus; the remedy is removal or moving off the compromised name, never an upgrade of the same artifact. Versions the registry has since pulled are listed but not scored",
"packages": [],
"red_flag": false,
"assessed_packages": 6,
"malicious_packages": 0,
"direct_malicious_packages": 0,
"withdrawn_malicious_packages": 0,
"installable_malicious_packages": 0
},
"components": [
{
"key": "no_dependency_reported_as_a_malicious_package",
"name": "No dependency reported as a malicious package",
"detail": "no dependency is reported as a malicious package",
"points": 100,
"status": "met",
"details": [
{
"code": "no_malicious_dependencies",
"params": {}
}
],
"max_points": 100
}
]
},
{
"key": "high_risk_jurisdiction_exposure",
"band": "excellent",
"name": "High-Risk Jurisdiction Exposure",
"note": "Only high-confidence self-published location evidence affects this multiplier. Ambiguous matches are review-only; country evidence is not proof of nationality, citizenship, legal registration, malicious intent, or sanctions status.",
"notes": [
{
"code": "jurisdiction_evidence_limits",
"params": {}
}
],
"value": 100,
"inputs": {
"meaning": "self-published location evidence; not nationality or citizenship",
"red_flag": false,
"exposures": [],
"policy_countries": [
"Russia",
"Iran",
"North Korea"
],
"review_only_matches": 0,
"assessed_self_published_locations": 3
},
"components": [
{
"key": "policy_exposure_multiplier",
"name": "Policy exposure multiplier",
"detail": "no confirmed policy-scope location match",
"points": 100,
"status": "met",
"details": [
{
"code": "jurisdiction_no_match",
"params": {}
}
],
"max_points": 100
}
]
}
],
"description": "Are visible security and supply-chain practices strong, with no malicious dependency and no unresolved high-risk jurisdiction exposure?"
},
{
"key": "ai_readiness",
"band": "moderate",
"name": "AI Readiness",
"value": 66,
"weight": 0,
"metrics": [
{
"key": "ai_agent_context",
"band": "at_risk",
"name": "Agent context & guidance",
"note": null,
"notes": [],
"value": 40,
"inputs": {
"has_llms_txt": false,
"legible_history_share": 0.96,
"agent_instruction_files": [],
"agent_instruction_max_bytes": null
},
"components": [
{
"key": "agent_instructions",
"name": "Agent instructions",
"detail": "no CLAUDE.md / AGENTS.md / editor rules",
"points": 0,
"status": "missed",
"details": [
{
"code": "no_agent_instructions",
"params": {}
}
],
"max_points": 45
},
{
"key": "machine_readable_docs_llms_txt",
"name": "Machine-readable docs (llms.txt)",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 15
},
{
"key": "legible_commit_history",
"name": "Legible commit history",
"detail": "96 of 100 human commits state their intent (structured subject or explanatory body)",
"points": 40,
"status": "met",
"details": [
{
"code": "legible_history",
"params": {
"legible": 96,
"sampled": 100
}
}
],
"max_points": 40
}
]
},
{
"key": "ai_verify_loop",
"band": "good",
"name": "Verify loop (build / test / typecheck)",
"note": null,
"notes": [],
"value": 82,
"inputs": {
"has_nix": false,
"has_tests": true,
"lockfiles": [
"go.sum"
],
"has_dockerfile": false,
"typed_language": true,
"bootstrap_files": [],
"has_devcontainer": false,
"has_linter_config": true,
"typecheck_configs": [],
"agent_commit_share": 0,
"toolchain_manifests": [
"go.mod"
],
"dependency_bot_commit_share": 0
},
"components": [
{
"key": "one_command_bootstrap",
"name": "One-command bootstrap",
"detail": "go.mod (toolchain convention, no task runner)",
"points": 12.6,
"status": "partial",
"details": [
{
"code": "toolchain_convention",
"params": {
"files": "go.mod"
}
}
],
"max_points": 18
},
{
"key": "automated_tests",
"name": "Automated tests",
"detail": null,
"points": 22,
"status": "met",
"details": [],
"max_points": 22
},
{
"key": "lint_format_config",
"name": "Lint / format config",
"detail": ".golangci.yml",
"points": 11,
"status": "met",
"details": [
{
"code": "file_list",
"params": {
"files": ".golangci.yml"
}
}
],
"max_points": 11
},
{
"key": "static_type_checking",
"name": "Static type checking",
"detail": "Go (statically typed)",
"points": 11,
"status": "met",
"details": [
{
"code": "statically_typed_language",
"params": {
"language": "Go"
}
}
],
"max_points": 11
},
{
"key": "reproducible_environment",
"name": "Reproducible environment",
"detail": "lockfile",
"points": 10,
"status": "met",
"details": [
{
"code": "file_list",
"params": {
"files": "lockfile"
}
}
],
"max_points": 10
},
{
"key": "demonstrated_agent_practice",
"name": "Demonstrated agent practice",
"detail": "no agent-authored commits among the last 100",
"points": 0,
"status": "missed",
"details": [
{
"code": "no_agent_authored_commits",
"params": {
"sampled": 100
}
}
],
"max_points": 10
},
{
"key": "automated_maintenance",
"name": "Automated maintenance",
"detail": "dependency automation configured, none observed in the sampled commits",
"points": 5,
"status": "partial",
"details": [
{
"code": "dependency_bot_config_only",
"params": {}
}
],
"max_points": 8
},
{
"key": "openssf_scorecard_pinned_dependencies",
"name": "OpenSSF Scorecard: Pinned-Dependencies",
"detail": "all dependencies are pinned",
"points": 10,
"status": "met",
"details": [],
"max_points": 10
}
]
},
{
"key": "ai_code_legibility",
"band": "excellent",
"name": "Code legibility for models",
"note": null,
"notes": [],
"value": 99,
"inputs": {
"primary_language": "Go",
"largest_source_bytes": 110794,
"source_files_sampled": 193,
"oversized_source_files": 3
},
"components": [
{
"key": "type_checkable_code",
"name": "Type-checkable code",
"detail": "Go (statically typed)",
"points": 45,
"status": "met",
"details": [
{
"code": "statically_typed_language",
"params": {
"language": "Go"
}
}
],
"max_points": 45
},
{
"key": "manageable_file_sizes",
"name": "Manageable file sizes",
"detail": "3/193 source files over 60KB",
"points": 54.1,
"status": "partial",
"details": [
{
"code": "oversized_source_files",
"params": {
"kb": 60,
"sampled": 193,
"oversized": 3
}
}
],
"max_points": 55
}
]
},
{
"key": "ai_interfaces",
"band": "at_risk",
"name": "Machine-readable interfaces",
"note": null,
"notes": [],
"value": 40,
"inputs": {
"example_dirs": [
"examples"
],
"has_mcp_signal": false,
"api_schema_files": []
},
"components": [
{
"key": "api_schema_openapi_graphql_proto",
"name": "API schema (OpenAPI/GraphQL/proto)",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 40
},
{
"key": "mcp_server",
"name": "MCP server",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 20
},
{
"key": "runnable_examples",
"name": "Runnable examples",
"detail": "examples",
"points": 40,
"status": "met",
"details": [
{
"code": "file_list",
"params": {
"files": "examples"
}
}
],
"max_points": 40
}
]
}
],
"description": "How well is the repo equipped to be developed and maintained with AI coding agents? An independent, experimental badge — weight 0.0, so it is surfaced on its own and does not affect the overall health score."
}
],
"metrics_version": "1.13.0"
},
"warnings": [
"Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token"
],
"report_type": "repository",
"generated_at": "2026-07-27T14:07:38.529562Z",
"schema_version": "0.27.0",
"badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/l/linkdata/jaws.svg",
"full_name": "linkdata/jaws",
"license_state": "standard",
"license_spdx": "MIT"
}