Commit Graph
500 Commits
Author SHA1 Message Date
tiennm99 d07f48d69e test(newsletter): add a golden-output parity suite
Captures the Go engine's stdout for 26 deterministic cases, commits those
outputs as fixtures, and asserts the JavaScript engine reproduces them through
the real CLI surface, exit codes included. This is both the migration's proof of
correctness and the regression net the engine never had.

Every fixture was generated from the Go engine rather than written by hand, and
reviewed before commit so no existing bug became a permanent contract. Each
behaviour that a JavaScript-idiomatic rewrite would silently break has a case
that goes red when the naive implementation is reintroduced; verified by
mutating the engine and re-running.

Content-scanning commands are pointed at a fixture tree through the subprocess
working directory. Both engines already resolve content/post from the process
CWD, so neither needed a test-only configuration knob, and the suite is proven
isolated from the real content by renaming it away and re-running.

Cases that depend on live upstreams are opt-in behind NEWSLETTER_NET=1: a
publication's feed window moves, and a suite that goes red on someone else's
outage teaches people to ignore red. The deterministic tier reaches the network
only through .invalid hosts, which never resolve, so it runs offline in about
two seconds.

The golden files are compared byte for byte, so scripts/newsletter is pinned to
LF; the repository otherwise checks out CRLF, which would break every comparison
on a fresh clone.
2026-09-18 16:24:16 +07:00
tiennm99 fb292d444d feat(newsletter): port the engine to JavaScript
Translates the seven-subcommand engine to Node ESM, one module per former Go
file, invoked as `node scripts/newsletter <command>` from the repo root. The Go
implementation stays in place for now so parity can be measured against it.

Hand-rolled HTML and XML regexes give way to cheerio, which removes the manual
string surgery in caption extraction and covers RSS and sitemap XML through
xmlMode without a second parser. fetch-via-defuddle gains a local extraction
stage ahead of the defuddle.md proxy; the proxy stays, because fetching from a
third IP is the whole point when this machine's IP is the blocked one. Both
stages now emit the same YAML frontmatter plus body, and an extraction that
looks like a bot challenge counts as a local failure so the proxy still runs.

Behaviour is preserved where it is load-bearing rather than where it is merely
idiomatic: query strings are rebuilt by string surgery so surviving parameters
keep their original order and encoding, duplicate detection stays an index loop
with byte-offset boundary checks so a prefix of a stored URL is not a false
match, empty optional fields are omitted rather than emitted as "", a deep-crawl
miss reports cutoff as null rather than dropping the key, bullet filtering counts
code points, tag counts keep their six-column alignment, and a malformed percent
sequence falls back to the raw substring.

The printer lives in its own module so a command module never imports the
dispatcher: index.js runs main() at module scope, so that cycle would execute
the CLI as a side effect of any import. Missing dependencies report one
actionable line instead of a module-resolution stack trace, and a reader that
closes early exits quietly instead of raising EPIPE.
2026-09-18 16:23:58 +07:00
tiennm99 a396c1fb40 refactor(scripts): unify module extensions on .js
The root package.json already declares "type": "module", so a .js file here is
ESM and the .mjs suffix only restates what the manifest states. Renames the three
GitHub Actions scripts and follows every reference, including the six JSDoc
import("...") type annotations that would otherwise degrade to any without
throwing.
2026-09-18 16:23:36 +07:00
tiennm99 2d35361ddd feat(projects): drop in-development namespaces from the demo list 2026-09-18 13:21:41 +07:00
tiennm99 aa1834f1bc refactor(projects): rename the demo list identifiers to match the page 2026-09-18 13:18:37 +07:00
tiennm99 552ea60a65 docs(projects): reword section headings and intros 2026-09-18 13:13:13 +07:00
tiennm99 1350aea058 docs(projects): rewrite projects page intros in a lighter voice 2026-09-18 13:04:36 +07:00
github-actions[bot] 89707097bc chore(projects): refresh projects list 2026-09-18 12:58:43 +07:00
tiennm99 bbda4851a8 refactor(projects): build the projects list on the octokit SDK
Replace hand-rolled REST paging, GraphQL cursor loops and header plumbing with
octokit, which also brings request retry and rate-limit backoff. Introduces the
repo's first package.json, lockfile and ESLint flat config; the Hugo site build
itself still needs no Node step.

Output verified against the previous implementation: identical counts and rows
apart from live star drift on four upstream repos.
2026-09-18 12:28:11 +07:00
tiennm99 7e46fde418 docs(plans): record script language evaluation for repo scripts 2026-09-18 12:24:44 +07:00
tiennm99 4656fbed46 feat(projects): split projects page into starred, linked and contributed lists
Resolve owned namespaces from org memberships with the admin role instead of
assuming a fixed owner list, so repos under orgs I own are included.

- starred: owned public non-fork repos with at least one star, by stars then
  most recent push
- linked: owned public non-fork repos exposing a homepage or Pages URL, by most
  recent push
- contributed: public repos owned by others, by my merged PR count then most
  recent push

Private repos are excluded everywhere; forks are excluded from the owned lists
because their homepage belongs to upstream. Split the generator into API access
and rendering modules.
2026-09-18 12:24:44 +07:00
github-actions[bot] 98461e768a chore(projects): refresh projects list 2026-09-18 02:56:03 +07:00
github-actions[bot] ba4be1c23b chore(projects): refresh projects list 2026-09-17 02:47:26 +07:00
tiennm99 a2f8298eda feat(newsletter): report post entry tally after each URL add 2026-09-11 17:33:46 +07:00
tiennm99 531fc01623 docs(newsletter): add newsletter 132 2026-09-11 17:33:46 +07:00
tiennm99 60e76cfbde docs(newsletter): add newsletter 131
Drop the key-points bullet list from newsletter article entries; summaries are prose paragraphs only.
2026-09-11 09:45:02 +07:00
tiennm99 49cd340f3d docs(newsletter): remove two Redis leaderboard entries from newsletter 130 2026-09-10 11:20:41 +07:00
tiennm99 19c75e0f0b docs(newsletter): add newsletter 130 2026-09-10 10:37:39 +07:00
github-actions[bot] d974792712 chore(projects): refresh projects list 2026-09-10 02:32:48 +07:00
tiennm99 5dfd2ad8a9 docs(skills): document fetch fallback chain and pinned target post
Add a reader-proxy tier after defuddle, stop skipping URLs that only fail a
plain classifier fetch, let a session pin an earlier draft as the target post,
and tighten Substack image candidate handling (dedupe, stable numbering,
reuse a resolved source post across a batch).
2026-09-09 09:25:35 +07:00
tiennm99 2b2826a0cb docs(newsletter): add newsletter 129 2026-09-09 09:19:25 +07:00
tiennm99 7735cc5517 chore: remove checked-in claude settings 2026-09-07 09:26:55 +07:00
github-actions[bot] 611916df0a chore(projects): refresh projects list 2026-09-01 04:41:05 +07:00
tiennm99 b43dde6203 chore: follow the miti99 -> blog repo rename 2026-08-31 17:30:27 +07:00
tiennm99 431c5b3beb docs(post): point taskbus source link at the jvm-labs lab 2026-08-31 16:09:58 +07:00
tiennm99 8f0928768f docs(referral): rebrand ClaudeKit to AgentKit
Update the referral link to https://agentkit.best/?ref=BWA910UK and expand
the description with current kits, pricing, and the desktop app, noting the
former ClaudeKit name.
2026-08-28 11:14:35 +07:00
tiennm99 756fbbb876 docs(newsletter): add newsletter 128 2026-08-28 10:21:45 +07:00
github-actions[bot] 3afa64bd3c chore(projects): refresh projects list 2026-08-28 08:12:06 +07:00
github-actions[bot] d0478b5deb chore(projects): refresh projects list 2026-08-26 00:21:32 +07:00
github-actions[bot] 8e2b20492b chore(projects): refresh projects list 2026-08-19 00:16:55 +07:00
tiennm99 1794b68538 fix(scripts): dedup URLs stored in markdown autolink form
The duplicate-check boundary set had '<' but not '>', so a URL stored
as <https://…> (autolink, used by older posts) was never detected as a
duplicate. Inherited from the JS engine; found while validating the Go
port against 2020-era posts.
2026-08-18 21:43:57 +07:00
tiennm99 10272495f5 refactor(scripts): migrate newsletter engine from Node to Go
One Go binary with a subcommand per former script, invoked as
'go run ./scripts/newsletter <command>' from repo root. Stdlib only;
publications config embedded via go:embed (go run recompiles on edit).
Behavior parity verified side-by-side before deleting the JS —
see plans/reports/parity-260818-newsletter-go-migration-report.md.

Permissions: allow 'Bash(go *)', drop 'Bash(node *)' and the redundant
'Write(content)' rule (Edit rules cover all file-editing tools).
2026-08-18 21:43:57 +07:00
github-actions[bot] 805189de8a chore(projects): refresh projects list 2026-08-14 00:47:31 +07:00
github-actions[bot] 2e8901307d chore(projects): refresh projects list 2026-08-12 00:46:41 +07:00
github-actions[bot] f20feacc6f chore(projects): refresh projects list 2026-08-11 00:41:49 +07:00
github-actions[bot] 0ff47f0aae chore(projects): refresh projects list 2026-08-09 00:19:32 +07:00
github-actions[bot] e9401f53a6 chore(projects): refresh projects list 2026-08-08 00:38:43 +07:00
github-actions[bot] f9decc9e14 chore(projects): refresh projects list 2026-08-06 01:14:17 +07:00
tiennm99 877e36eee0 docs(referral): add OrcaRouter offers 2026-08-05 09:28:17 +07:00
github-actions[bot] e410081924 chore(projects): refresh projects list 2026-08-05 01:21:14 +07:00
tiennm99 ffed070bb3 chore(claude): drop plugin toggles and use auto permission mode 2026-07-30 23:20:01 +07:00
tiennm99 eba1b66bd6 docs(blog): update 2026-07-29 post 2026-07-29 19:14:25 +07:00
github-actions[bot] c9779a3914 chore(projects): refresh projects list 2026-07-28 01:17:10 +07:00
github-actions[bot] 82a3be3b06 chore(projects): refresh projects list 2026-07-27 00:52:30 +07:00
github-actions[bot] cbaf9f4820 chore(projects): refresh projects list 2026-07-26 00:48:10 +07:00
github-actions[bot] 530139befe chore(projects): refresh projects list 2026-07-25 01:09:30 +07:00
tiennm99 eb81462f93 docs(newsletter): add newsletter 126 2026-07-23 21:16:10 +07:00
tiennm99 001fada225 docs(referral): remove expired friend pass 2026-07-20 23:24:15 +07:00
github-actions[bot] e746cd59d0 chore(projects): refresh projects list 2026-07-20 00:46:33 +07:00
github-actions[bot] 45bc1ac33b chore(projects): refresh projects list 2026-07-19 00:45:05 +07:00