lintful's recent activity
-
Comment on Contempt culture and its currency in ~tech
-
Comment on AT Protocol (Bluesky): Call for Developer Projects in ~comp
lintful I find myself nerdsniped :| Most of the web dev people I followed on Twitter have moved over to Bluesky so this is good timing.Entire Social App Categories
I find myself nerdsniped :|
Most of the web dev people I followed on Twitter have moved over to Bluesky so this is good timing.
-
Comment on What programming/technical projects have you been working on? in ~comp
lintful (edited )LinkI found myself needing customizable output from tests in JS (TS), and the test framework I normally use doesn't support it. I looked at the Node builtin test runner and some other popular ones,...I found myself needing customizable output from tests in JS (TS), and the test framework I normally use doesn't support it. I looked at the Node builtin test runner and some other popular ones, and wasn't happy with the APIs and capabilities, so I starting writing a new test framework for myself because it sounds like a fun diversion with some concrete benefits to my workflow, and implementing one isn't that difficult.
It's not open source yet but it will be soon, I have a few major blockers remaining. I'm naming it Zest I think.
I had the first iteration working and fairly well-tested using itself, but now I'm changing the API to support nested groups - this meant rewriting a lot of the internals. I'm at 1600 LOC of tests, trying to be as thorough as I can because bugs in a test framework seem extra bad.
The first iteration of the API was mostly inspired by uvu:
import {test, suite} from 'scope/zest'; // Tests are defined like this: test('test name', () => {}); // Tests can be grouped at the top-level: const test_in_suite = suite('suite name'); test_in_suite('test name', () => {});
Creates:
file some.test.ts ✓ test name suite name ✓ test name
And I'm changing it to also support nested groups:
import {test} from 'scope/zest'; test('test name', () => {}); test.group('group name', () => { test('test name', () => {}); test.group('nested group name', () => { test('test name', () => {}); }); test('test name 2', () => {}); // can create any tests/groups (including using `await`) // except inside `test('name', cb)` callbacks, // and duplicate names for tests/groups // are disallowed in the same group scope }); const group = test.group('also works');
Creates:
group some.test.ts ✓ test name group name ✓ test name nested group name ✓ test name ✓ test name 2 also works
Basically like Deno's BDD maybe with some specific choices and inspiration from uvu. The visual weight of
test(
andtest.group(
feel good to me.Each test module is implicitly in a group with the name of the relative path to its file. This gives us a really simple hierarchical data structure of tests and nestable groups, where tests must be leaf nodes.
I think the main idea that I haven't seen in other frameworks (I didn't look very hard, I could be way wrong and this is common, thoughts/references welcome) is that I'm separating the execution of test files into two distinct phases:
- First is the planning phase where discovery/initialization/import/registration happens - the TS test modules are imported, and the tests and groups all get registered. By the time a test module finishes importing, its tests and groups are immutably defined in a plan object in the registry. This means
test.group()
calls are executed synchronously during module import and once the module exits, you cannot add new groups or tests through the normal APIs. - Second is running the tests from a plan. Any subset of the tests or groups may be run, maybe multiple times like in a watcher. Results are statefully assembled in the runner as it runs tests, and you can have multiple runners for the same registry.
So a plan is created when modules import, and with top-level
await
and async group callbacks, you can construct plans with arbitrary code at startup. A plan can be run multiple times in a long-running process, and its data is exposed in full fidelity at runtime or output to JSON without running any tests. I think I could streamline refreshing plans with a reused worker thread and really nudge users to doing setup in hooks (which are skipped when just reading the registry, so e.g. you wouldn't need to wait on a db connection just to get the test structure), but tests themselves will probably run with full process isolation at the granularity of groups/tests that you can configure.Code can restructure or modify plans as desired. I think this will work well when adding opt-in parallelization.
There's a lot of implications to this design that I'm still thinking through. One tradeoff is that you cannot create tests or groups after the module has been imported. Trying will throw an error, but maybe that could be relaxed and you just have some caveats for the capabilities around dynamic tests.
I think this is more complicated to deal with e.g. file changes and plan refreshing, but possibly more powerful in that it abstracts away the filesystem and makes the framework's internals a nice exposed API. (rule of least power) I'm ignorant of how most test frameworks work, but e.g. Jest doesn't have good reporting of registered tests, and I browsed some popular frameworks without seeing what I was looking for.
With the restrictions and complexity come some nice benefits, because you know tests and groups won't be created on the fly, so you can treat it like data, even reactively. It's easier to do things with the test metadata, like making a UI with fine-grained handles on it - I have an early prototype, this probably excites me the most.
There's a programmatic API available so you can make your own "test root" objects like
test
above. It's perhaps strange to havetest
be so magical but I liked the convenience of a single import. I'm trying to make it as modular as I can, and most behavior should be pluggable.It has a plugin architecture with hooks for all of the events, so it's easy to add multiple custom reporters or other integrations. Right now I just use them for outputting data - in one case logging event/summary info to stdout, and the other outputting JSON to stdout for the plans and results. All outputs are implemented as plugins so the core stays minimal. I'm thinking about maybe adding control flow mechanisms like error handling capabilities to the plugin hooks. (e.g. result mapping like enhancing errors, stopping/changing events, I think I want to give the hooks as much control as possible and let userland deal with the mess)
It's been a lot of fun to make, and I'm still open to rethinking the API to get it as nice/flexible as possible, input welcome!
- First is the planning phase where discovery/initialization/import/registration happens - the TS test modules are imported, and the tests and groups all get registered. By the time a test module finishes importing, its tests and groups are immutably defined in a plan object in the registry. This means
-
Comment on OpenAI, Google and Anthropic are struggling to build more advanced AI in ~tech
lintful (edited )Link ParentLLMs can respond with text of almost any length and style and word choice given the right prompt though. You can even ask them to mess up the grammar and spelling, something they tend to be nearly...LLMs can respond with text of almost any length and style and word choice given the right prompt though. You can even ask them to mess up the grammar and spelling, something they tend to be nearly flawless at.
Also most people only have experience with "instruct" models trained for conversation, but base models just continue a text, making it even lower effort to hide in plain sight.
A consequence to this is any "AI detection" software is snake oil. They only catch the lowest effort stuff and false positives and negatives are impossible.
-
Comment on They stole my voice with AI in ~tech
lintful (edited )Link ParentI expect it'll be trivial to create blends of two+ voices instead of just copying one. Considering two different kinds of intentions - cheap commercial content, and leveraging the likeness of...I expect it'll be trivial to create blends of two+ voices instead of just copying one. Considering two different kinds of intentions - cheap commercial content, and leveraging the likeness of someone to trade off their brand - I think the former is going to be pervasive. For the latter, California just passed a bill trying to address this, and yeah, per usual smaller creatives likely won't fare well.
-
Comment on Telegram messaging app CEO Pavel Durov arrested in France in ~tech
lintful Telegram doesn't e2ee group chats though, so this argument seems tangential.Telegram doesn't e2ee group chats though, so this argument seems tangential.
-
Comment on React, Electron, and LLMs have a common purpose: the labour arbitrage theory of dev tool popularity in ~comp
lintful (edited )LinkThis article weirdly dodges discussion of technical merits, ecosystem alignment, and network effects. It abstracts out a theory without engaging with the content. Here's an example: No mention of...This article weirdly dodges discussion of technical merits, ecosystem alignment, and network effects. It abstracts out a theory without engaging with the content.
Here's an example:
CoffeeScript disappeared seemingly overnight while TypeScript is a juggernaut that has pushed pretty much every other “compile to JavaScript” language out of the market.
No mention of ES6 or what TypeScript did well, instead it tries to generalize patterns as if the technical details, developer incentives, user demographics beyond managers, and many other contextual factors don't matter.
The better starting points IMO are the technology adoption curve and the technology itself in the ecosystem's context. I wasn't looking to be negative and I'm politically sympathetic but this reads to me like thin punditry from motivated reasoning. I'm not denying that labor arbitrage happens but the story does not land for me - as others pointed out, technology adoption is not as simple as this suggests and the agents in play are not represented in a way that matches my work experience.
-
Comment on Reddit, AI spam bots explore new ways to show ads in your feed in ~tech
lintful (edited )Link ParentGreat post, and I'll add something I find both galling and motivating. Most usecases, except for some like video and search that are more capital-intensive (and unlike the real-world bar, but like...Great post, and I'll add something I find both galling and motivating. Most usecases, except for some like video and search that are more capital-intensive (and unlike the real-world bar, but like many physical examples), the costs of operation for most of these social spaces are very low, at least in centralized or small-scale contexts. (e.g. large-scale federation can get more expensive quickly) To me this gap presents a loud opportunity. Disintermediation of the profiteers is economically feasible. It's also pretty important because these forces broadly control how we use technology to connect with each other.
-
Comment on Unity appoints former EA and Zynga executive Matthew Bromberg as its new CEO in ~games
lintful Given the company's antics I assume he'll be tasked by the board with squeezing the ecosystem for revenue in the short to medium term. It's not primarily about building the best future of the...Given the company's antics I assume he'll be tasked by the board with squeezing the ecosystem for revenue in the short to medium term. It's not primarily about building the best future of the technology. Not an enviable position for anyone who values the game developers and players more than shareholder value. He sounds like a good fit.
-
Comment on The real danger to civilisation isn't runaway AI it's runaway capitalism (2017) in ~society
lintful A key similarity may be that corporations are goal-directed entities whose reward function is reduced to an overly simplistic metric that is decoupled from human wellbeing. Most other groups don't...A key similarity may be that corporations are goal-directed entities whose reward function is reduced to an overly simplistic metric that is decoupled from human wellbeing. Most other groups don't optimize for profit like that. This reward function underlies enshittification.
My long experience in the JS community been a pervasive culture of people making jokes at the language's expense, and using it anyway because it's what runs in websites.
The most popular image meme I can think of is this one about JavasScript: The Good Parts, where the joke is that the language is mostly bad parts.
Probably the most-heavily memed video ever made about the language, Wat, is about how baffling the language can be. The subtitle on that page is "This talk does not represent anyone's actual opinion." - it's not mean spirited, but it's shining the light exactly where it deserves.
Criticism has always been abundant, and it's part of why TypeScript is so loved - an excellent type system combined with the modern changes to the language (the bulk of which landed in 2015) make for (finally) a language that doesn't make you lose your hair.
Far more influential is the fact that each of the dominant players on the web has to agree before anything changes on the platform, and backwards compatibility has been guaranteed since 1995, a situation few languages have to deal with. This conservatism has been a boon in some ways because it means the ideas that do tend to make it in are more fully baked and accepted across the industry - but up until 2015 the situation was dire and the people working with it were painfully aware, and the self-deprecating culture continues.