diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 04ba375..34b3c3a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,9 +40,6 @@ jobs: - name: Format run: bun run fmt:check - - name: Test - run: bun test - - name: Smoke the CLI run: bun src/cli.ts --help diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0b5731e..7694294 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -38,9 +38,6 @@ jobs: - name: Typecheck run: bun run typecheck - - name: Test - run: bun test - - name: Smoke the CLI run: bun src/cli.ts --help diff --git a/README.md b/README.md index 2fdd3b1..9a529f9 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ _actual issues, tells the coding agent exactly how + what to fix_ **[more catche ### What you get -- **Your taste, not the model's.** Code is judged against a `CORPUS.md`: a [taste pack](#taste-packs) ("code like dtolnay / DHH / antirez …") or your own best files +- **Your taste, not the model's.** Code is judged against a `CORPUS.md` of your own best files - **On your personal Codex plan.** stupify reviews with [Codex](https://github.com/openai/codex), running on the $20-$200/month plan. API usage is roughly 50x more expensive, enjoy the subsidized tokens while they last - **Slop, named.** Code review is cheap. Taste is expensive. Codify the goodies, let the LLM pattern match @@ -59,18 +59,9 @@ The reviews run on Codex. On exe.dev that's a keyless **LLM integration**: it fr the VM holds no API key and your plan is billed instead. Link one once at [exe.dev/integrations](https://exe.dev/integrations) and provisioning attaches it for you -## Taste packs +## Your taste -Don't have a corpus yet? Borrow one. Pick a programmer whose code you'd point a new hire at and review and write like them, or compose several: - -[dtolnay](packs/dtolnay.md) · [DHH](packs/dhh.md) · [antirez](packs/antirez.md) · -[Sindre Sorhus](packs/sindre-sorhus.md) · [Rich Harris](packs/rich-harris.md) · -[zod](packs/zod.md) · [Mitchell Hashimoto](packs/mitchell-hashimoto.md) · -[Tanner Linsley](packs/tanner-linsley.md) · [Simon Willison](packs/simon-willison.md) · -[devshorts](packs/devshorts.md) · [Jarred Sumner](packs/jarred-sumner.md) · [browse all →](packs) - -Each pack is concrete principles plus commit-pinned exemplar files. Or **bring your own**: point stupify at the -files you _wish_ all your code looked like, and it scaffolds a `.review/` in your repo: +Point stupify at the files you _wish_ all your code looked like, and it scaffolds a `.review/` in your repo: ```bash npx @stupify/cli init src/best.ts src/clean-service.ts # inlines them; you add one line of "why" each diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index a7cf301..4d51f95 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -16,7 +16,7 @@ generic engines, and the taste they read. A `.review/` _inside the repo being reviewed_ is version-controlled with the code it judges, visible in code review, and tuned through a normal PR, the same way you'd change a lint config. When a repo has none, both -engines fall back to `~/.stupify/.review`, which the CLI assembles from [taste packs](../packs). The reviewer +engines fall back to `~/.stupify/.review`, a global taste you place by hand. The reviewer reads it fresh from `origin/main` on every sweep, so a merged rubric change is live immediately. ## Two ends of the loop: prevent, then detect diff --git a/package.json b/package.json index 4f7cf5c..bb770e8 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,6 @@ "src/hand-written-prompts.ts", "src/sweep", ".review", - "packs", "README.md", "LICENSE" ], @@ -41,7 +40,6 @@ }, "scripts": { "typecheck": "tsc -p tsconfig.json", - "test": "bun test", "lint": "oxlint", "fmt": "oxfmt", "fmt:check": "oxfmt --check", diff --git a/packs/antirez.md b/packs/antirez.md deleted file mode 100644 index 768194a..0000000 --- a/packs/antirez.md +++ /dev/null @@ -1,318 +0,0 @@ -## Code like Salvatore Sanfilippo / antirez (@antirez) · comments that earn their keep - -Readable C, of all things — because the comments do real work. The top of a header is not a summary of what the file does; it is a working diagram of the data structure that the reader can verify their understanding against before touching a line of code. Every function states its contract and error return in its leading comment. When code is non-obvious — memory layouts, pointer arithmetic, algorithmic cases — the comment draws the state before and after so the reader does not have to simulate the machine. Nothing obvious gets a comment; nothing non-obvious goes unexplained. The result is that the implementation and the explanation are always in sync because they are written as one thing. - -### `rax.h` — the data structure drawn in ASCII as the opening spec -[source](https://github.com/antirez/rax/blob/1927550cb218ec3c3dda8b39d82d1d019bf0476d/rax.h) -```c -/* Representation of a radix tree as implemented in this file, that contains - * the strings "foo", "foobar" and "footer" after the insertion of each - * word. When the node represents a key inside the radix tree, we write it - * between [], otherwise it is written between (). - * - * This is the vanilla representation: - * - * (f) "" - * \ - * (o) "f" - * \ - * (o) "fo" - * \ - * [t b] "foo" - * / \ - * "foot" (e) (a) "foob" - * / \ - * "foote" (r) (r) "fooba" - * / \ - * "footer" [] [] "foobar" - * - * However, this implementation implements a very common optimization where - * successive nodes having a single child are "compressed" into the node - * itself as a string of characters, each representing a next-level child, - * and only the link to the node representing the last character node is - * provided inside the representation. So the above representation is turned - * into: - * - * ["foo"] "" - * | - * [t b] "foo" - * / \ - * "foot" ("er") ("ar") "foob" - * / \ - * "footer" [] [] "foobar" - * - * However this optimization makes the implementation a bit more complex. - * For instance if a key "first" is added in the above radix tree, a - * "node splitting" operation is needed, since the "foo" prefix is no longer - * composed of nodes having a single child one after the other. This is the - * above tree and the resulting node splitting after this event happens: - * - * - * (f) "" - * / - * (i o) "f" - * / \ - * "firs" ("rst") (o) "fo" - * / \ - * "first" [] [t b] "foo" - * / \ - * "foot" ("er") ("ar") "foob" - * / \ - * "footer" [] [] "foobar" - * - * Similarly after deletion, if a new chain of nodes having a single child - * is created (the chain must also not include nodes that represent keys), - * it must be compressed back into a single node. - * - */ -``` -The reader sees three variants of the data structure — vanilla, compressed, after a split — before encountering a single typedef. This is not decoration; it is the spec. The code that follows is a direct translation of what the diagram commits to. - -### `rax.h` — the node struct with its exact byte layout described inside the field comment -[source](https://github.com/antirez/rax/blob/1927550cb218ec3c3dda8b39d82d1d019bf0476d/rax.h) -```c -#define RAX_NODE_MAX_SIZE ((1<<29)-1) -typedef struct raxNode { - uint32_t iskey:1; /* Does this node contain a key? */ - uint32_t isnull:1; /* Associated value is NULL (don't store it). */ - uint32_t iscompr:1; /* Node is compressed. */ - uint32_t size:29; /* Number of children, or compressed string len. */ - /* Data layout is as follows: - * - * If node is not compressed we have 'size' bytes, one for each children - * character, and 'size' raxNode pointers, point to each child node. - * Note how the character is not stored in the children but in the - * edge of the parents: - * - * [header iscompr=0][abc][a-ptr][b-ptr][c-ptr](value-ptr?) - * - * if node is compressed (iscompr bit is 1) the node has 1 children. - * In that case the 'size' bytes of the string stored immediately at - * the start of the data section, represent a sequence of successive - * nodes linked one after the other, for which only the last one in - * the sequence is actually represented as a node, and pointed to by - * the current compressed node. - * - * [header iscompr=1][xyz][z-ptr](value-ptr?) - * - * Both compressed and not compressed nodes can represent a key - * with associated data in the radix tree at any level (not just terminal - * nodes). - * - * If the node has an associated key (iskey=1) and is not NULL - * (isnull=0), then after the raxNode pointers poiting to the - * children, an additional value pointer is present (as you can see - * in the representation above as "value-ptr" field). - */ - unsigned char data[]; -} raxNode; -``` -The struct comment is not a summary of the fields — it shows the exact byte layout for both compressed and uncompressed cases, including the optional trailing `value-ptr`. Every `raxNodeCurrentLength` macro and every `memmove` in the implementation becomes legible because this comment told you what the bytes look like. - -### `rax.c` — `raxStackPush`: error handling via `errno` + OOM flag + return 0 -[source](https://github.com/antirez/rax/blob/1927550cb218ec3c3dda8b39d82d1d019bf0476d/rax.c) -```c -/* Push an item into the stack, returns 1 on success, 0 on out of memory. */ -static inline int raxStackPush(raxStack *ts, void *ptr) { - if (ts->items == ts->maxitems) { - if (ts->stack == ts->static_items) { - ts->stack = rax_malloc(sizeof(void*)*ts->maxitems*2); - if (ts->stack == NULL) { - ts->stack = ts->static_items; - ts->oom = 1; - errno = ENOMEM; - return 0; - } - memcpy(ts->stack,ts->static_items,sizeof(void*)*ts->maxitems); - } else { - void **newalloc = rax_realloc(ts->stack,sizeof(void*)*ts->maxitems*2); - if (newalloc == NULL) { - ts->oom = 1; - errno = ENOMEM; - return 0; - } - ts->stack = newalloc; - } - ts->maxitems *= 2; - } - ts->stack[ts->items] = ptr; - ts->items++; - return 1; -} -``` -The error convention is consistent across the whole library: return `0` on failure, set `errno = ENOMEM`, and set an `oom` flag on the containing struct so callers that defer error checking can detect the problem later. The realloc branch carefully assigns into a temp variable before overwriting `ts->stack` so the original pointer is not lost on failure. - -### `rax.c` — `raxNewNode` + `raxNew`: typical constructor pair with partial cleanup -[source](https://github.com/antirez/rax/blob/1927550cb218ec3c3dda8b39d82d1d019bf0476d/rax.c) -```c -/* Allocate a new non compressed node with the specified number of children. - * If datafiled is true, the allocation is made large enough to hold the - * associated data pointer. - * Returns the new node pointer. On out of memory NULL is returned. */ -raxNode *raxNewNode(size_t children, int datafield) { - size_t nodesize = sizeof(raxNode)+children+raxPadding(children)+ - sizeof(raxNode*)*children; - if (datafield) nodesize += sizeof(void*); - raxNode *node = rax_malloc(nodesize); - if (node == NULL) return NULL; - node->iskey = 0; - node->isnull = 0; - node->iscompr = 0; - node->size = children; - return node; -} - -/* Allocate a new rax and return its pointer. On out of memory the function - * returns NULL. */ -rax *raxNew(void) { - rax *rax = rax_malloc(sizeof(*rax)); - if (rax == NULL) return NULL; - rax->numele = 0; - rax->numnodes = 1; - rax->head = raxNewNode(0,0); - if (rax->head == NULL) { - rax_free(rax); - return NULL; - } else { - return rax; - } -} -``` -Each constructor comment states what it returns on OOM before the code begins. When the two-step `raxNew` allocation fails at step two, it frees what it already allocated and returns NULL — no half-constructed objects escape, no `goto` needed. - -### `rax.c` — `raxLowWalk`: the core walk loop with explanatory inline comments -[source](https://github.com/antirez/rax/blob/1927550cb218ec3c3dda8b39d82d1d019bf0476d/rax.c) -```c -static inline size_t raxLowWalk(rax *rax, unsigned char *s, size_t len, raxNode **stopnode, raxNode ***plink, int *splitpos, raxStack *ts) { - raxNode *h = rax->head; - raxNode **parentlink = &rax->head; - - size_t i = 0; /* Position in the string. */ - size_t j = 0; /* Position in the node children (or bytes if compressed).*/ - while(h->size && i < len) { - debugnode("Lookup current node",h); - unsigned char *v = h->data; - - if (h->iscompr) { - for (j = 0; j < h->size && i < len; j++, i++) { - if (v[j] != s[i]) break; - } - if (j != h->size) break; - } else { - /* Even when h->size is large, linear scan provides good - * performances compared to other approaches that are in theory - * more sounding, like performing a binary search. */ - for (j = 0; j < h->size; j++) { - if (v[j] == s[i]) break; - } - if (j == h->size) break; - i++; - } - - if (ts) raxStackPush(ts,h); /* Save stack of parent nodes. */ - raxNode **children = raxNodeFirstChildPtr(h); - if (h->iscompr) j = 0; /* Compressed node only child is at index 0. */ - memcpy(&h,children+j,sizeof(h)); - parentlink = children+j; - j = 0; /* If the new node is compressed and we do not - iterate again (since i == l) set the split - position to 0 to signal this node represents - the searched key. */ - } - debugnode("Lookup stop node is",h); - if (stopnode) *stopnode = h; - if (plink) *plink = parentlink; - if (splitpos && h->iscompr) *splitpos = j; - return i; -} -``` -Two loop variables — `i` (position in the query string) and `j` (position within the current node) — are named for their local role and annotated at their declaration. The non-obvious performance call (linear scan beats binary search here) is justified in a comment rather than assumed. The function returns the number of characters consumed, leaving callers to infer success or stop-position from a single integer. - -### `rax-test.c` — `fuzzTest`: testing by running two implementations in lockstep -[source](https://github.com/antirez/rax/blob/1927550cb218ec3c3dda8b39d82d1d019bf0476d/rax-test.c) -```c -/* Perform a fuzz test, returns 0 on success, 1 on error. */ -int fuzzTest(int keymode, size_t count, double addprob, double remprob) { - hashtable *ht = htNew(); - rax *rax = raxNew(); - - printf("Fuzz test in mode %d [%zu]: ", keymode, count); - fflush(stdout); - - /* Perform random operations on both the dictionaries. */ - for (size_t i = 0; i < count; i++) { - unsigned char key[1024]; - uint32_t keylen; - - /* Insert element. */ - if ((double)rc4rand()/RAND_MAX < addprob) { - keylen = int2key((char*)key,sizeof(key),i,keymode); - void *val = (void*)(unsigned long)rc4rand(); - /* Stress NULL values more often, they use a special encoding. */ - if (!(rc4rand() % 100)) val = NULL; - int retval1 = htAdd(ht,key,keylen,val); - int retval2 = raxInsert(rax,key,keylen,val,NULL); - if (retval1 != retval2) { - printf("Fuzz: key insertion reported mismatching value in HT/RAX\n"); - return 1; - } - } - - /* Remove element. */ - if ((double)rc4rand()/RAND_MAX < remprob) { - keylen = int2key((char*)key,sizeof(key),i,keymode); - int retval1 = htRem(ht,key,keylen); - int retval2 = raxRemove(rax,key,keylen,NULL); - if (retval1 != retval2) { - printf("Fuzz: key deletion of '%.*s' reported mismatching " - "value in HT=%d RAX=%d\n", - (int)keylen,(char*)key,retval1, retval2); - printf("%p\n", raxFind(rax,key,keylen)); - printf("%p\n", raxNotFound); - return 1; - } - } - } - - /* Check that count matches. */ - if (ht->numele != raxSize(rax)) { - printf("Fuzz: HT / RAX keys count mismatch: %lu vs %lu\n", - (unsigned long) ht->numele, - (unsigned long) raxSize(rax)); - return 1; - } - printf("%lu elements inserted\n", (unsigned long)ht->numele); - - /* Check that elements match. */ - raxIterator iter; - raxStart(&iter,rax); - raxSeek(&iter,"^",NULL,0); - - size_t numkeys = 0; - while(raxNext(&iter)) { - void *val1 = htFind(ht,iter.key,iter.key_len); - void *val2 = raxFind(rax,iter.key,iter.key_len); - if (val1 != val2) { - printf("Fuzz: HT=%p, RAX=%p value do not match " - "for key %.*s\n", - val1, val2, (int)iter.key_len,(char*)iter.key); - return 1; - } - numkeys++; - } - - /* Check that the iterator reported all the elements. */ - if (ht->numele != numkeys) { - printf("Fuzz: the iterator reported %lu keys instead of %lu\n", - (unsigned long) numkeys, - (unsigned long) ht->numele); - return 1; - } - - raxStop(&iter); - raxFree(rax); - htFree(ht); - return 0; -} -``` -The test file ships its own minimal hash table — not a test double, but a second correct implementation that always tells the truth. Every operation is applied to both structures simultaneously and their return values compared immediately. The verification phase then iterates the radix tree and cross-checks every key against the hash table. This is differential testing as a first-class design: correctness is proven by agreement, not by hardcoded expected values. diff --git a/packs/devshorts.md b/packs/devshorts.md deleted file mode 100644 index ec1d7c5..0000000 --- a/packs/devshorts.md +++ /dev/null @@ -1,251 +0,0 @@ -## Code like devshorts (@devshorts) · DI + branded types - -Every domain concept gets its own tiny wrapper type — a `QueueName`, never a raw `String` — so a primitive can never flow where a named concept belongs. Dependencies wire through small, single-purpose Guice modules enumerated explicitly at one auditable composition root. Interfaces are single-method contracts or thin behavioral surfaces; implementations receive all collaborators via `@Inject` constructors and never reach for anything not handed to them. `Clock` is injected so any time-dependent decision is seam-testable without touching the system clock. Fail fast and loud: exceptions are typed, named, and carry the operation context so call sites can log and re-throw exactly once. - -### `QueueName.java` — branded value type: a raw string cannot masquerade as a `QueueName` -[source](https://github.com/paradoxical-io/cassieq/blob/3856962f13e5f7d84893a2ef274d08016b2c828b/model/src/main/java/io/paradoxical/cassieq/model/QueueName.java) -```java -@Immutable -@XmlJavaTypeAdapter(value = QueueName.XmlAdapter.class) -@JsonSerialize(using = QueueName.JsonSerializeAdapter.class) -@JsonDeserialize(using = QueueName.JsonDeserializeAdapater.class) -public final class QueueName extends StringValue { - protected QueueName(final String value) { - super(value); - } - - public static QueueName valueOf(@NonNull String value) { - return new QueueName(StringUtils.trimToEmpty(value)); - } - - public static QueueName valueOf(@NonNull StringValue value) { - return QueueName.valueOf(value.get()); - } -``` -The constructor is `protected` — the only entry point is `valueOf`, which rejects nulls via `@NonNull` and normalizes whitespace. The type carries its own JSON/XML adapters so serialization never silently degrades back to a plain string. Dozens of types in this repo follow the same pattern: `AccountName`, `AccountKey`, `MessageId`, `BucketPointer` — every domain boundary is named and enforced. - -### `DataAccessModule.java` — composition root for data access: one module, one concern, every binding explicit -[source](https://github.com/paradoxical-io/cassieq/blob/3856962f13e5f7d84893a2ef274d08016b2c828b/core/src/main/java/io/paradoxical/cassieq/modules/DataAccessModule.java) -```java -public class DataAccessModule extends AbstractModule { - - @Override protected void configure() { - install(new FactoryModuleBuilder() - .implement(MessageRepository.class, MessageRepositoryImpl.class) - .build(MessageRepoFactory.class)); - - install(new FactoryModuleBuilder() - .implement(PointerRepository.class, PointerRepositoryImpl.class) - .build(PointerRepoFactory.class)); - - install(new FactoryModuleBuilder() - .implement(MonotonicRepository.class, MonotonicRepoImpl.class) - .build(MonotonicRepoFactory.class)); - - - install(new FactoryModuleBuilder() - .implement(QueueRepository.class, QueueRepositoryImpl.class) - .build(QueueRepositoryFactory.class)); - - bind(AccountRepository.class).to(AccountRepositoryImpl.class); - - bind(DataContextFactory.class).to(DataContextFactoryImpl.class); - } -} -``` -Every repository interface is bound to exactly one implementation, no scanning, no reflection magic. Each `FactoryModuleBuilder` installs a per-queue-scoped assisted-inject factory so callers get queue-partitioned repos without the module knowing about call sites. Swapping an impl for tests means installing a different module — the interface and the binding stay orthogonal. - -### `MessageRepository.java` — interface shape: thin contract, default impl on the interface itself -[source](https://github.com/paradoxical-io/cassieq/blob/3856962f13e5f7d84893a2ef274d08016b2c828b/core/src/main/java/io/paradoxical/cassieq/dataAccess/interfaces/MessageRepository.java) -```java -public interface MessageRepository { - void putMessage(final Message message, final Duration initialInvisibility) throws ExistingMonotonFoundException; - - default void putMessage(final Message message) throws ExistingMonotonFoundException { - putMessage(message, Duration.ZERO); - } - - /** - * Strictly consumes, applies no business logic - * @param message - * @param duration - * @return - */ - Optional rawConsumeMessage(final Message message, final Duration duration); - - boolean ackMessage(final Message message); - - default List getMessages(final BucketPointer bucketPointer) { - return getBucketContents(bucketPointer).stream().filter(Message::isNotSpecial).collect(toList()); - } - - List getBucketContents(final BucketPointer bucketPointer); - - boolean finalize(RepairBucketPointer bucketPointer); - - boolean tombstone(final ReaderBucketPointer bucketPointer); - - Message getMessage(final MessagePointer pointer); - - Optional tombstoneExists(final BucketPointer bucketPointer); - - void deleteAllMessages(BucketPointer bucket); - - Optional updateMessage(MessageUpdateRequest message); - - boolean finalizedExists(BucketPointer bucketPointer); -} -``` -The interface carries its own default convenience overload (`putMessage` without duration defaults to `Duration.ZERO`) and its own stream filter (`getMessages` strips special markers from `getBucketContents`). Every method returns `Optional` or a boolean rather than throwing on not-found — the decision about what to do with absence stays with the caller. - -### `ReaderImpl.java` — injected `Clock` does real work, not decoration -[source](https://github.com/paradoxical-io/cassieq/blob/3856962f13e5f7d84893a2ef274d08016b2c828b/core/src/main/java/io/paradoxical/cassieq/workers/reader/ReaderImpl.java) -```java - private Optional getAndMark(ReaderBucketPointer currentBucket, Duration invisiblity) { - - while (true) { - final List allMessages = dataContext.getMessageRepository().getMessages(currentBucket); - - final boolean allComplete = allMessages.stream().allMatch(m -> m.isAcked() || m.isNotVisible(clock)); - - if (allComplete) { - if (allMessages.size() == queueDefinition.getBucketSize().get() || monotonPastBucket(currentBucket)) { - tombstone(currentBucket); - - currentBucket = advanceBucket(currentBucket); - - continue; - } - else { - // bucket not ready to be closed yet, but all current messages processed - return Optional.empty(); - } - } - - final Optional foundMessage = findRandom(allMessages.stream().filter(m -> m.isNotAcked() && m.isVisible(clock)).collect(Collectors.toList())); - - if (!foundMessage.isPresent()) { - return Optional.empty(); - } - - final ConsumableMessage consumableMessage = new ConsumableMessage(foundMessage.get(), invisiblity, Source.Reader); - - Optional consumedMessage = tryConsume(consumableMessage); - - if (consumedMessage.isPresent()) { - return consumedMessage; - } - - // loop again - } - } -``` -`clock` is injected via the constructor — not `System.currentTimeMillis()` hidden inside `Message`. Every visibility check (`isNotVisible(clock)`, `isVisible(clock)`) passes the seam through, so a test can inject a fake clock and advance time to exercise tombstoning and bucket advancement without sleeping. The `while (true)` is intentional: optimistic CAS — if another consumer wins `tryConsume`, loop and find the next visible message. - -### `QueueResource.java` — error handling shape: log once, wrap in typed exception, never swallow -[source](https://github.com/paradoxical-io/cassieq/blob/3856962f13e5f7d84893a2ef274d08016b2c828b/core/src/main/java/io/paradoxical/cassieq/discoverable/resources/api/v1/QueueResource.java) -```java - public Response ackMessage( - @StringTypeValid @PathParam("queueName") QueueName queueName, - @NotNull @QueryParam("popReceipt") String popReceiptRaw) { - - final QueueDefinition definition = lookupQueueDefinition(queueName); - - final PopReceipt popReceipt = PopReceipt.valueOf(popReceiptRaw); - - boolean messageAcked; - - try { - messageAcked = getReaderFactory().forQueue(getAccountName(), definition) - .ackMessage(popReceipt); - } - catch (Exception e) { - logger.error(e, "Error"); - throw new QueueInternalServerError("AckMessage", queueName, e); - } - - if (messageAcked) { - return Response.noContent().build(); - } - - throw new ConflictException("AckMessage", "The message is already being reprocessed."); - } -``` -The pattern repeats identically across every handler: parse the typed domain value at the boundary (`PopReceipt.valueOf`), execute, log-and-rethrow infrastructure errors as a named typed exception with the operation name and queue context, then convert the boolean result to the right HTTP status. No silent fallbacks, no catch-and-continue. - -### `TestBase.java` — test harness: the injector is module-swappable, the clock is field-level and passed in -[source](https://github.com/paradoxical-io/cassieq/blob/3856962f13e5f7d84893a2ef274d08016b2c828b/core/src/test/java/io/paradoxical/cassieq/unittests/TestBase.java) -```java - @Getter(AccessLevel.PROTECTED) - private final TestClock testClock = new TestClock(); - - public TestBase() { - - } - - protected TestQueueContext createTestQueueContext(QueueName queueName) { - return new TestQueueContext(testAccountName, queueName, getDefaultInjector()); - } - - @Before - public void beforeTest() { - hazelCastModule = new HazelcastTestModule("test_" + UUID.randomUUID()); - } - - @After - public void afterTest() { - hazelCastModule.close(); - } - - protected TestQueueContext setupTestContext(QueueDefinition queueDefinition) { - return new TestQueueContext(createQueue(queueDefinition), getDefaultInjector()); - } - - protected TestQueueContext setupTestContext(String queueName) { - return setupTestContext(queueName, 20); - } - - protected TestQueueContext setupTestContext(String queueName, int bucketSize) { - final QueueName queue = QueueName.valueOf(queueName); - final QueueDefinition queueDefinition = QueueDefinition.builder() - .accountName(testAccountName) - .queueName(queue) - .strictFifo(true) - .bucketSize(BucketSize.valueOf(bucketSize)) - .build(); - return setupTestContext(queueDefinition); - } -``` -`TestClock` is a protected field on every test, and `TestClockModule` is always merged in last so it overrides production `ClockModule`. Tests get a real Guice injector — not mocks — with the environment, Hazelcast, and clock modules swapped in. The queue name itself is a `QueueName.valueOf(...)`, never a raw string, even in test setup. - -### `ReaderTester.java` — test shape: time-travel via `getTestClock().tickSeconds`, domain assertions on message content -[source](https://github.com/paradoxical-io/cassieq/blob/3856962f13e5f7d84893a2ef274d08016b2c828b/core/src/test/java/io/paradoxical/cassieq/unittests/tests/queueSemantics/ReaderTester.java) -```java - @Test - public void initial_inivs_is_respected() throws Exception { - final TestQueueContext testContext = setupTestContext("initial_inivs_is_respected", 10); - - testContext.putMessage(0, "msg1"); - testContext.putMessage(400000, "msg2"); - testContext.putMessage(300000, "msg3"); - testContext.putMessage(200000, "msg4"); - testContext.putMessage(0, "msg5"); - - testContext.readAndAckMessage("msg1"); - testContext.readAndAckMessage("msg5"); - - getTestClock().tickSeconds(200000L); - - testContext.readAndAckMessage("msg4"); - - getTestClock().tickSeconds(100000L); - - testContext.readAndAckMessage("msg3"); - - getTestClock().tickSeconds(100000L); - - testContext.readAndAckMessage("msg2"); - - } -``` -Tests read like a scenario script: put messages with explicit invisibility durations, tick the injected clock by known increments, then assert that exactly the right message becomes visible. No sleeps, no mocking of the reader, no stubbing of the queue — it's the real implementation running against a real in-memory Cassandra, with time as the only controlled variable. diff --git a/packs/dhh.md b/packs/dhh.md deleted file mode 100644 index 2241212..0000000 --- a/packs/dhh.md +++ /dev/null @@ -1,258 +0,0 @@ -## Code like DHH (@dhh) · controllers that tell the story - -DHH writes code that reads like an outline of intent, not a transcript of implementation. Controllers are one-line-per-action tables of contents; the hard work lives in named model methods, scopes, and concerns that carry the domain vocabulary. Cross-cutting rules — auth, scoping, rate-limiting — are declared once in a concern and applied by name at the call site, never repeated inline. Error handling follows the same philosophy: domain errors become named exception classes, guard clauses become named predicates, and failure paths redirect or respond with a status, never swallow. Tests are integration-first, hitting real HTTP endpoints with fixture data and asserting on the full response, not on implementation details. - -### `sessions_controller.rb` — the controller as a table of contents; no logic leaks in - -[source](https://github.com/basecamp/once-campfire/blob/8d3c2bbd2be070008a275330efbee1001fd202dc/app/controllers/sessions_controller.rb) -```ruby -class SessionsController < ApplicationController - allow_unauthenticated_access only: %i[ new create ] - rate_limit to: 10, within: 3.minutes, only: :create, with: -> { render_rejection :too_many_requests } - - before_action :ensure_user_exists, only: :new - - def new - end - - def create - if user = User.active.authenticate_by(email_address: params[:email_address], password: params[:password]) - start_new_session_for user - redirect_to post_authenticating_url - else - render_rejection :unauthorized - end - end - - def destroy - remove_push_subscription - terminate_current_session - redirect_to root_url - end - - private - def ensure_user_exists - redirect_to first_run_url if User.none? - end - - def render_rejection(status) - flash.now[:alert] = "Too many requests or unauthorized." - render :new, status: status - end - - def remove_push_subscription - if endpoint = params[:push_subscription_endpoint] - Push::Subscription.destroy_by(endpoint: endpoint, user_id: Current.user.id) - end - end -end -``` -Every action is two lines or fewer; policy declarations (`allow_unauthenticated_access`, `rate_limit`) sit at the top of the class like annotations. The only conditional in `create` names both branches — `authenticate_by` and `render_rejection` — and neither branch has any implementation detail inside the action body. - -### `room.rb` — association extensions as named domain operations, not ad-hoc query logic - -[source](https://github.com/basecamp/once-campfire/blob/8d3c2bbd2be070008a275330efbee1001fd202dc/app/models/room.rb) -```ruby -class Room < ApplicationRecord - has_many :memberships, dependent: :delete_all do - def grant_to(users) - room = proxy_association.owner - Membership.insert_all(Array(users).collect { |user| { room_id: room.id, user_id: user.id, involvement: room.default_involvement } }) - end - - def revoke_from(users) - destroy_by user: users - end - - def revise(granted: [], revoked: []) - transaction do - grant_to(granted) if granted.present? - revoke_from(revoked) if revoked.present? - end - end - end - - has_many :users, through: :memberships - has_many :messages, dependent: :destroy - - belongs_to :creator, class_name: "User", default: -> { Current.user } - - scope :opens, -> { where(type: "Rooms::Open") } - scope :closeds, -> { where(type: "Rooms::Closed") } - scope :directs, -> { where(type: "Rooms::Direct") } - scope :without_directs, -> { where.not(type: "Rooms::Direct") } -``` -Membership management is embedded directly in the `has_many` block as named operations — `grant_to`, `revoke_from`, `revise` — so call sites say `room.memberships.grant_to(users)` and never need to know about `insert_all` or the involvement default. Scopes are aligned in columns so the type taxonomy reads as a visual table. - -### `message/searchable.rb` — a concern that owns one responsibility end-to-end via lifecycle hooks - -[source](https://github.com/basecamp/once-campfire/blob/8d3c2bbd2be070008a275330efbee1001fd202dc/app/models/message/searchable.rb) -```ruby -module Message::Searchable - extend ActiveSupport::Concern - - included do - after_create_commit :create_in_index - after_update_commit :update_in_index - after_destroy_commit :remove_from_index - - scope :search, ->(query) { joins("join message_search_index idx on messages.id = idx.rowid").where("idx.body match ?", query).ordered } - end - - private - def create_in_index - execute_sql_with_binds "insert into message_search_index(rowid, body) values (?, ?)", id, plain_text_body - end - - def update_in_index - execute_sql_with_binds "update message_search_index set body = ? where rowid = ?", plain_text_body, id - end - - def remove_from_index - execute_sql_with_binds "delete from message_search_index where rowid = ?", id - end - - def execute_sql_with_binds(*statement) - self.class.connection.execute self.class.sanitize_sql(statement) - end -end -``` -The concern declares its full contract — create, update, destroy, and query — in one place. The lifecycle hook names (`create_in_index`, `update_in_index`, `remove_from_index`) match the SQL intent so closely that reading the `included` block gives you the full mental model without opening any private method. - -### `opengraph/fetch.rb` — errors as named domain exception classes; validation decomposed into single-check predicates - -[source](https://github.com/basecamp/once-campfire/blob/8d3c2bbd2be070008a275330efbee1001fd202dc/app/models/opengraph/fetch.rb) -```ruby -class Opengraph::Fetch - ALLOWED_DOCUMENT_CONTENT_TYPE = "text/html" - MAX_BODY_SIZE = 5.megabytes - MAX_REDIRECTS = 10 - - class TooManyRedirectsError < StandardError; end - class RedirectDeniedError < StandardError; end - - def fetch_document(url, ip: RestrictedHTTP::PrivateNetworkGuard.resolve(url.host)) - request(url, Net::HTTP::Get, ip: ip) do |response| - return body_if_acceptable(response) - end - end - - def fetch_content_type(url, ip: RestrictedHTTP::PrivateNetworkGuard.resolve(url.host)) - request(url, Net::HTTP::Head, ip: ip) do |response| - return response["Content-Type"] - end - end - - private - def request(url, request_class, ip:) - MAX_REDIRECTS.times do - Net::HTTP.start(url.host, url.port, ipaddr: ip, use_ssl: url.scheme == "https") do |http| - http.request request_class.new(url) do |response| - if response.is_a?(Net::HTTPRedirection) - url, ip = resolve_redirect(response["location"]) - else - yield response - end - end - end - end - - raise TooManyRedirectsError - end - - def resolve_redirect(location) - url = URI.parse(location) - raise RedirectDeniedError unless url.is_a?(URI::HTTP) - [ url, RestrictedHTTP::PrivateNetworkGuard.resolve(url.host) ] - end - - def body_if_acceptable(response) - size_restricted_body(response) if response_valid?(response) - end - - def size_restricted_body(response) - # We've already checked the Content-Length header, to try to avoid reading - # the body of any large responses. But that header could be wrong or - # missing. To be on the safe side, we'll read the body in chunks, and bail - # if it runs over our size limit. - StringIO.new.tap do |body| - response.read_body do |chunk| - return nil if body.string.bytesize + chunk.bytesize > MAX_BODY_SIZE - body << chunk - end - end.string - end - - def response_valid?(response) - status_valid?(response) && content_type_valid?(response) && content_length_valid?(response) - end -``` -`TooManyRedirectsError` and `RedirectDeniedError` are named domain events, not rescued `StandardError`s. Validation is decomposed into three single-boolean predicates (`status_valid?`, `content_type_valid?`, `content_length_valid?`) composed by `response_valid?` — each validation predicate is exactly one line and one idea. - -### `messages_controller_test.rb` — integration tests that hit the HTTP boundary with fixture identity, asserting on observable output - -[source](https://github.com/basecamp/once-campfire/blob/8d3c2bbd2be070008a275330efbee1001fd202dc/test/controllers/messages_controller_test.rb) -```ruby -class MessagesControllerTest < ActionDispatch::IntegrationTest - setup do - host! "once.campfire.test" - - sign_in :david - @room = rooms(:watercooler) - @messages = @room.messages.ordered.to_a - end - - test "index returns the last page by default" do - get room_messages_url(@room) - - assert_response :success - ensure_messages_present @messages.last - end - - test "index returns a page before the specified message" do - get room_messages_url(@room, before: @messages.third) - - assert_response :success - ensure_messages_present @messages.first, @messages.second - ensure_messages_not_present @messages.third, @messages.fourth, @messages.fifth - end - - test "index returns a page after the specified message" do - get room_messages_url(@room, after: @messages.third) - - assert_response :success - ensure_messages_present @messages.fourth, @messages.fifth - ensure_messages_not_present @messages.first, @messages.second, @messages.third - end - - test "index returns no_content when there are no messages" do - @room.messages.destroy_all - - get room_messages_url(@room) - - assert_response :no_content - end - - test "get renders a single message belonging to the user" do - message = @room.messages.where(creator: users(:david)).first - -``` -Tests are `ActionDispatch::IntegrationTest` — real HTTP, real fixture rows, real response assertions. Each test names one behavior in prose (`"ensure non-admin can't update a message belonging to another user"`), sets up identity with a fixture symbol (`sign_in :jz`), fires the endpoint, and asserts on the HTTP response or the rendered DOM. No mocks of the subject under test. - -### `user/role.rb` — one-method concerns; `can_administer?` as a readable policy predicate - -[source](https://github.com/basecamp/once-campfire/blob/8d3c2bbd2be070008a275330efbee1001fd202dc/app/models/user/role.rb) -```ruby -module User::Role - extend ActiveSupport::Concern - - included do - enum :role, %i[ member administrator bot ] - end - - def can_administer?(record = nil) - administrator? || self == record&.creator || record&.new_record? - end -end -``` -The entire authorization predicate for the application is one method, eleven words. `administrator?` comes from the enum; `self == record&.creator` is "you own it"; `record&.new_record?` is "it hasn't been saved yet." No boolean columns, no permission tables, no role-checking DSL — three `||`-joined clauses that any reader can audit in one glance. diff --git a/packs/dtolnay.md b/packs/dtolnay.md deleted file mode 100644 index 5df6807..0000000 --- a/packs/dtolnay.md +++ /dev/null @@ -1,338 +0,0 @@ -## Code like David Tolnay (@dtolnay) · the API that disappears - -Every public surface is the minimum needed for correct use. Where Rust lacks specialization, thin pointers, or stable vtable dispatch, dtolnay simulates them through carefully typed seams — autoref dispatch, hand-rolled vtable structs, `repr(transparent)` newtype stacks — all of which vanish at the call site. Errors carry full context and backtrace, but expose no internals. Macros generate exactly the `impl` a skilled human would write by hand, including graceful fallback on parse failure. `#[cold]` appears on every error-path constructor so branch prediction never penalizes the happy path. - -### `kind.rs` — autoref dispatch as a zero-cost substitute for specialization -[source](https://github.com/dtolnay/anyhow/blob/841522b2aa09732fecee40804440d2c35c68c480/src/kind.rs) -```rust -pub struct Adhoc; - -#[doc(hidden)] -pub trait AdhocKind: Sized { - #[inline] - fn anyhow_kind(&self) -> Adhoc { - Adhoc - } -} - -impl AdhocKind for &T where T: ?Sized + Display + Debug + Send + Sync + 'static {} - -impl Adhoc { - #[cold] - pub fn new(self, message: M) -> Error - where - M: Display + Debug + Send + Sync + 'static, - { - Error::construct_from_adhoc(message, backtrace!()) - } -} - -pub struct Trait; - -#[doc(hidden)] -pub trait TraitKind: Sized { - #[inline] - fn anyhow_kind(&self) -> Trait { - Trait - } -} - -impl TraitKind for E where E: Into {} - -impl Trait { - #[cold] - pub fn new(self, error: E) -> Error - where - E: Into, - { - error.into() - } -} -``` -`AdhocKind` is implemented on `&T` (one extra autoref), so when `T: Into` the more-specific `TraitKind` impl on `T` wins method resolution without any `#[feature(specialization)]`. The entire dispatch is zero-cost and the `#[cold]` hint steers branch prediction away from both constructors; the macro call site is simply `(&error).anyhow_kind().new(error)` — the mechanism is invisible. - -### `ptr.rs` — typed pointer wrappers that encode ownership in the type system -[source](https://github.com/dtolnay/anyhow/blob/841522b2aa09732fecee40804440d2c35c68c480/src/ptr.rs) -```rust -#[repr(transparent)] -pub struct Own -where - T: ?Sized, -{ - pub ptr: NonNull, -} - -unsafe impl Send for Own where T: ?Sized {} - -unsafe impl Sync for Own where T: ?Sized {} - -impl Copy for Own where T: ?Sized {} - -impl Clone for Own -where - T: ?Sized, -{ - fn clone(&self) -> Self { - *self - } -} - -impl Own -where - T: ?Sized, -{ - pub fn new(ptr: Box) -> Self { - Own { - ptr: unsafe { NonNull::new_unchecked(Box::into_raw(ptr)) }, - } - } - - pub fn cast(self) -> Own { - Own { - ptr: self.ptr.cast(), - } - } - - pub unsafe fn boxed(self) -> Box { - unsafe { Box::from_raw(self.ptr.as_ptr()) } - } - - pub fn by_ref(&self) -> Ref { - Ref { - ptr: self.ptr, - lifetime: PhantomData, - } - } - - pub fn by_mut(&mut self) -> Mut { - Mut { - ptr: self.ptr, - lifetime: PhantomData, - } - } -} -``` -`Own`, `Ref<'a, T>`, and `Mut<'a, T>` are three `repr(transparent)` wrappers around `NonNull` that encode ownership at compile time without fat-pointer overhead — the foundation that makes a thin `anyhow::Error` possible. The `CastTo` trait forces an explicit turbofish on every `.cast::()` call, making every erasure step visible in the source. - -### `error.rs` — hand-rolled vtable that keeps `Error` a thin pointer -[source](https://github.com/dtolnay/anyhow/blob/841522b2aa09732fecee40804440d2c35c68c480/src/error.rs) -```rust -struct ErrorVTable { - object_drop: unsafe fn(Own), - object_ref: unsafe fn(Ref) -> Ref, - #[cfg(any(feature = "std", not(anyhow_no_core_error)))] - object_boxed: unsafe fn(Own) -> Box, - #[cfg(any(feature = "std", not(anyhow_no_core_error)))] - object_reallocate_boxed: unsafe fn(Own) -> Box, - object_downcast: unsafe fn(Ref, TypeId) -> Option>, - object_drop_rest: unsafe fn(Own, TypeId), - #[cfg(all(not(error_generic_member_access), feature = "std"))] - object_backtrace: unsafe fn(Ref) -> Option<&Backtrace>, -} - -// Safety: requires layout of *e to match ErrorImpl. -unsafe fn object_drop(e: Own) { - // Cast back to ErrorImpl so that the allocator receives the correct - // Layout to deallocate the Box's memory. - let unerased_own = e.cast::>(); - drop(unsafe { unerased_own.boxed() }); -} - -// Safety: requires layout of *e to match ErrorImpl. -unsafe fn object_drop_front(e: Own, target: TypeId) { - // Drop the fields of ErrorImpl other than E as well as the Box allocation, - // without dropping E itself. This is used by downcast after doing a - // ptr::read to take ownership of the E. - let _ = target; - let unerased_own = e.cast::>>(); - drop(unsafe { unerased_own.boxed() }); -} -``` -`ErrorVTable` is a plain struct of function pointers, not a trait object — it lives in the same allocation as the error and keeps the outer `Error` a single-word thin pointer. Every field has a `// Safety:` invariant spelled out above the function, and `#[cfg]` gates expose only the slots the current feature set actually uses. - -### `context.rs` — private `mod ext` that unifies two error paths behind one trait -[source](https://github.com/dtolnay/anyhow/blob/841522b2aa09732fecee40804440d2c35c68c480/src/context.rs) -```rust -mod ext { - use super::*; - - pub trait StdError { - fn ext_context(self, context: C) -> Error - where - C: Display + Send + Sync + 'static; - } - - #[cfg(any(feature = "std", not(anyhow_no_core_error)))] - impl StdError for E - where - E: crate::StdError + Send + Sync + 'static, - { - fn ext_context(self, context: C) -> Error - where - C: Display + Send + Sync + 'static, - { - let backtrace = backtrace_if_absent!(&self); - Error::construct_from_context(context, self, backtrace) - } - } - - impl StdError for Error { - fn ext_context(self, context: C) -> Error - where - C: Display + Send + Sync + 'static, - { - self.context(context) - } - } -} - -impl Context for Result -where - E: ext::StdError + Send + Sync + 'static, -{ - fn context(self, context: C) -> Result - where - C: Display + Send + Sync + 'static, - { - // Not using map_err to save 2 useless frames off the captured backtrace - // in ext_context. - match self { - Ok(ok) => Ok(ok), - Err(error) => Err(error.ext_context(context)), - } - } -``` -`mod ext` is private and its `StdError` shadow-trait is never exported; the two impls (one for any `E: std::error::Error`, one for `anyhow::Error` itself) unify behind `ext::StdError` so the public `Context` blanket impl handles both cases with a single where-clause. The comment "Not using map_err to save 2 useless frames" is characteristic: even incidental backtrace noise is deliberately cut. - -### `expand.rs` — codegen that generates exactly the impl you'd write by hand -[source](https://github.com/dtolnay/thiserror/blob/7214e0e8331d76afbea7173d8a14997512ac8713/impl/src/expand.rs) -```rust -pub fn derive(input: &DeriveInput) -> TokenStream { - match try_expand(input) { - Ok(expanded) => expanded, - // If there are invalid attributes in the input, expand to an Error impl - // anyway to minimize spurious secondary errors in other code that uses - // this type as an Error. - Err(error) => fallback::expand(input, error), - } -} - -fn try_expand(input: &DeriveInput) -> Result { - let input = Input::from_syn(input)?; - input.validate()?; - Ok(match input { - Input::Struct(input) => impl_struct(input), - Input::Enum(input) => impl_enum(input), - }) -} -``` -`fallback::expand` ensures a broken `#[derive(Error)]` still emits a syntactically valid `Error` impl rather than flooding the user with secondary type errors — the macro recovers gracefully and reports exactly one error. The entry-point is four lines; all complexity is pushed down into `try_expand`, `impl_struct`, and `impl_enum`. - -### `valid.rs` — exhaustive validation with precise, attribute-spanned error messages -[source](https://github.com/dtolnay/thiserror/blob/7214e0e8331d76afbea7173d8a14997512ac8713/impl/src/valid.rs) -```rust -fn check_non_field_attrs(attrs: &Attrs) -> Result<()> { - if let Some(from) = &attrs.from { - return Err(Error::new_spanned( - from.original, - "not expected here; the #[from] attribute belongs on a specific field", - )); - } - if let Some(source) = &attrs.source { - return Err(Error::new_spanned( - source.original, - "not expected here; the #[source] attribute belongs on a specific field", - )); - } - if let Some(backtrace) = &attrs.backtrace { - return Err(Error::new_spanned( - backtrace, - "not expected here; the #[backtrace] attribute belongs on a specific field", - )); - } - if attrs.transparent.is_some() { - if let Some(display) = &attrs.display { - return Err(Error::new_spanned( - display.original, - "cannot have both #[error(transparent)] and a display attribute", - )); - } - if let Some(fmt) = &attrs.fmt { - return Err(Error::new_spanned( - fmt.original, - "cannot have both #[error(transparent)] and #[error(fmt = ...)]", - )); - } - } else if let (Some(display), Some(_)) = (&attrs.display, &attrs.fmt) { - return Err(Error::new_spanned( - display.original, - "cannot have both #[error(fmt = ...)] and a format arguments attribute", - )); - } - - Ok(()) -} -``` -Every error is anchored to the token it was found on (`Error::new_spanned(from.original, …)`) so the compiler underlines exactly the wrong attribute. The function returns early on the first violation and the messages name the correct placement, not just the problem — the user never has to guess where the attribute belongs. - -### `fmt.rs` — inline unit tests co-located with the formatting implementation -[source](https://github.com/dtolnay/anyhow/blob/841522b2aa09732fecee40804440d2c35c68c480/src/fmt.rs) -```rust -#[cfg(test)] -mod tests { - use super::*; - use alloc::string::String; - - #[test] - fn one_digit() { - let input = "verify\nthis"; - let expected = " 2: verify\n this"; - let mut output = String::new(); - - Indented { - inner: &mut output, - number: Some(2), - started: false, - } - .write_str(input) - .unwrap(); - - assert_eq!(expected, output); - } - - #[test] - fn two_digits() { - let input = "verify\nthis"; - let expected = " 12: verify\n this"; - let mut output = String::new(); - - Indented { - inner: &mut output, - number: Some(12), - started: false, - } - .write_str(input) - .unwrap(); - - assert_eq!(expected, output); - } - - #[test] - fn no_digits() { - let input = "verify\nthis"; - let expected = " verify\n this"; - let mut output = String::new(); - - Indented { - inner: &mut output, - number: None, - started: false, - } - .write_str(input) - .unwrap(); - - assert_eq!(expected, output); - } -} -``` -Tests live inside `mod tests` in the same file as the code they exercise and construct the private `Indented` struct directly — no test helpers, no abstraction over the assertion. Each test is named for the variant it covers (`one_digit`, `two_digits`, `no_digits`), builds the exact input string, and compares against a verbatim expected output with whitespace counted by eye. diff --git a/packs/jarred-sumner.md b/packs/jarred-sumner.md deleted file mode 100644 index 5387733..0000000 --- a/packs/jarred-sumner.md +++ /dev/null @@ -1,301 +0,0 @@ -## Code like Jarred Sumner (@Jarred-Sumner) · perf as a correctness concern - -Every Sumner design decision answers the question: "what can the compiler prove, and what can I eliminate before the first byte runs?" Token membership becomes integer range comparison, keyword lookup becomes a comptime-sorted table bucketed by length, five parallel SIMD character checks fire before the allocator is touched at all, and an entire thread-pool's mutable state fits inside a single atomic `u32`. When the type system can enforce an invariant — illegal state unrepresentable, wrong-type format call caught at compile time, platform-specific code deleted by `comptime` rather than guarded by runtime `if` — that is always preferred over a defensive check at runtime. The fast path is also the obvious path. - -### `lexer_tables.zig` — naming convention and enum-as-range-checked classifier -[source](https://github.com/oven-sh/bun/blob/454e3b2884c2bfabfa424ebecc3e9a1a9ee32773/src/js_parser/lexer_tables.zig) -```zig -pub const T = enum(u8) { - t_end_of_file, - // close brace is here so that we can do comparisons against EOF or close brace in one branch - t_close_brace, - - t_syntax_error, - - // "#!/usr/bin/env node" - t_hashbang, - - // literals - t_no_substitution_template_literal, // contents are in lexer.string_literal ([]uint16) - t_numeric_literal, // contents are in lexer.number (float64) - t_string_literal, // contents are in lexer.string_literal ([]uint16) - t_big_integer_literal, // contents are in lexer.identifier (string) - - // pseudo-literals - t_template_head, // contents are in lexer.string_literal ([]uint16) - t_template_middle, // contents are in lexer.string_literal ([]uint16) - t_template_tail, // contents are in lexer.string_literal ([]uint16) - - // punctuation - t_ampersand, - t_ampersand_ampersand, - t_asterisk, - t_asterisk_asterisk, - t_at, - t_bar, - t_bar_bar, - t_caret, - t_close_bracket, - t_close_paren, -``` -Names are flat, predictable prefixes (`t_`) with full spelling — never `TokenKind.AssignPlusEq`, always `t_plus_equals`. Variants are ordered deliberately so contiguous integer ranges serve as O(1) set membership: `t_close_brace` sits at position 1 so `isCloseBraceOrEOF` is `@intFromEnum(self) <= 1`, and all assignment tokens are grouped so `isAssign` is a single two-ended range check — no switch, no hash. - -### `exact_size_matcher.zig` — comptime turns string comparison into integer equality -[source](https://github.com/oven-sh/bun/blob/454e3b2884c2bfabfa424ebecc3e9a1a9ee32773/src/bun_core/string/immutable/exact_size_matcher.zig) -```zig -pub fn ExactSizeMatcher(comptime max_bytes: usize) type { - switch (max_bytes) { - 1, 2, 4, 8, 12, 16 => {}, - else => { - @compileError("max_bytes must be 1, 2, 4, 8, 12, or 16."); - }, - } - - const T = std.meta.Int( - .unsigned, - max_bytes * 8, - ); - - return struct { - pub fn match(str: anytype) T { - switch (str.len) { - 1...max_bytes - 1 => { - var tmp: [max_bytes]u8 = undefined; - @memcpy(tmp[0..str.len], str); - @memset(tmp[str.len..], 0); - - return std.mem.readInt(T, &tmp, .little); - }, - max_bytes => { - return std.mem.readInt(T, str[0..max_bytes], .little); - }, - 0 => { - return 0; - }, - else => { - return std.math.maxInt(T); - }, - } - } -``` -`match()` reinterprets the incoming bytes as a single integer of width `max_bytes * 8`, chosen at compile time by `std.meta.Int`. A sibling `case()` does the same for a string literal at comptime, so `switch (match(token)) { case("if") => …, case("for") => … }` lowers to integer comparison — no `strcmp`, no per-character branching. The legal `max_bytes` widths are checked with `@compileError`, so an unsupported size fails the build rather than misbehaving at runtime. - -### `comptime_string_map.zig` — comptime-sorted keyword table, bucketed by length -[source](https://github.com/oven-sh/bun/blob/454e3b2884c2bfabfa424ebecc3e9a1a9ee32773/src/collections/comptime_string_map.zig) -```zig -/// Comptime string map optimized for small sets of disparate string keys. -/// Works by separating the keys by length at comptime and only checking strings of -/// equal length at runtime. -/// -/// `kvs` expects a list literal containing list literals or an array/slice of structs -/// where `.@"0"` is the `[]const u8` key and `.@"1"` is the associated value of type `V`. -/// TODO: https://github.com/ziglang/zig/issues/4335 -pub fn ComptimeStringMapWithKeyType(comptime KeyType: type, comptime V: type, comptime kvs_list: anytype) type { - const KV = struct { - key: []const KeyType, - value: V, - }; - - const precomputed = comptime blk: { - @setEvalBranchQuota(99999); - - var sorted_kvs: [kvs_list.len]KV = undefined; - const lenAsc = (struct { - fn lenAsc(context: void, a: KV, b: KV) bool { - _ = context; - if (a.key.len != b.key.len) { - return a.key.len < b.key.len; - } - // https://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-processing-an-unsorted-array - @setEvalBranchQuota(999999); - return std.mem.order(KeyType, a.key, b.key) == .lt; - } - }).lenAsc; - if (KeyType == u8) { - for (kvs_list, 0..) |kv, i| { - if (V != void) { - sorted_kvs[i] = .{ .key = kv.@"0", .value = kv.@"1" }; - } else { - sorted_kvs[i] = .{ .key = kv.@"0", .value = {} }; - } - } - } else { - @compileError("Not implemented for this key type"); - } - std.sort.pdq(KV, &sorted_kvs, {}, lenAsc); - const min_len = sorted_kvs[0].key.len; - const max_len = sorted_kvs[sorted_kvs.len - 1].key.len; - var len_indexes: [max_len + 1]usize = undefined; - var len: usize = 0; - var i: usize = 0; - - while (len <= max_len) : (len += 1) { - @setEvalBranchQuota(99999); - - // find the first keyword len == len - while (len > sorted_kvs[i].key.len) { - i += 1; - } - len_indexes[len] = i; - } - break :blk .{ - .min_len = min_len, - .max_len = max_len, - .sorted_kvs = sorted_kvs, - .len_indexes = len_indexes, - }; - }; -``` -The entire sort and index-building step runs at comptime inside `comptime blk:`; at runtime `get()` dispatches only into the slice of candidates whose `.len` already matches. The sorted-by-length invariant is documented inline with a link to the benchmark that confirmed it — motivation travels with the code. - -### `threading/ThreadPool.zig` — entire concurrency state in one atomic `packed struct(u32)` -[source](https://github.com/oven-sh/bun/blob/454e3b2884c2bfabfa424ebecc3e9a1a9ee32773/src/threading/ThreadPool.zig) -```zig -const Sync = packed struct(u32) { - /// Tracks the number of threads not searching for Tasks - idle: u14 = 0, - /// Tracks the number of threads spawned - spawned: u14 = 0, - /// What you see is what you get - unused: bool = false, - /// Used to not miss notifications while state = waking - notified: bool = false, - /// The current state of the thread pool - state: enum(u2) { - /// A notification can be issued to wake up a sleeping as the "waking thread". - pending = 0, - /// The state was notified with a signal. A thread is woken up. - /// The first thread to transition to `waking` becomes the "waking thread". - signaled, - /// There is a "waking thread" among us. - /// No other thread should be woken up until the waking thread transitions the state. - waking, - /// The thread pool was terminated. Start decremented `spawned` so that it can be joined. - shutdown, - } = .pending, -}; -``` -All thread-pool coordination — idle count, spawned count, a notification flag, and a four-state lifecycle — fits in exactly 32 bits, so every state transition is a single `cmpxchgWeak`. There are no separate mutexes, no condition variables protecting individual counters. The packed layout makes the invariant visible: `idle + spawned` fits in 28 bits, `state` takes 2, `notified` 1, leaving 1 unused — a reviewer can audit all legal transitions by reading one struct. - -### `css/error.zig` — generic parameterized error type with compile-time format guard -[source](https://github.com/oven-sh/bun/blob/454e3b2884c2bfabfa424ebecc3e9a1a9ee32773/src/css/error.zig) -```zig -/// An error with a source location. -pub fn Err(comptime T: type) type { - return struct { - /// The type of error that occurred. - kind: T, - /// The location where the error occurred. - loc: ?ErrorLocation, - - pub fn format( - this: @This(), - writer: *std.Io.Writer, - ) !void { - if (@hasDecl(T, "format")) { - return this.kind.format(writer); - } - @compileError("format not implemented for " ++ @typeName(T)); - } - - pub const toErrorInstance = @import("../css_jsc/error_jsc.zig").toErrorInstance; - - pub fn fromParseError(err: ParseError(ParserError), filename: []const u8) Err(ParserError) { - if (T != ParserError) { - @compileError("Called .fromParseError() when T is not ParserError"); - } - - const kind = switch (err.kind) { - .basic => |b| switch (b) { - .unexpected_token => |t| ParserError{ .unexpected_token = t }, - .end_of_input => ParserError.end_of_input, - .at_rule_invalid => |a| ParserError{ .at_rule_invalid = a }, - .at_rule_body_invalid => ParserError.at_rule_body_invalid, - .qualified_rule_invalid => ParserError.qualified_rule_invalid, - }, - .custom => |c| c, - }; - - return .{ - .kind = kind, - .loc = ErrorLocation{ - .filename = filename, - .line = err.location.line, - .column = err.location.column, - }, - }; - } -``` -`Err(T)` is a comptime generic: it wraps any error-kind type alongside an optional source location, and it enforces that callers only call `fromParseError` when `T == ParserError` — wrong-type calls are a compile error, not a runtime panic. The `format` method uses `@hasDecl` to delegate to the inner type but falls back to `@compileError` if the type hasn't implemented it, pushing the bug to build time rather than to a runtime crash or silent empty output. - -### `io/io.zig` — epoll tick loop: draining pending work then blocking on events -[source](https://github.com/oven-sh/bun/blob/454e3b2884c2bfabfa424ebecc3e9a1a9ee32773/src/io/io.zig) -```zig - pub fn tickEpoll(this: *Loop) void { - if (comptime !Environment.isLinux) { - @compileError("Epoll is Linux-Only"); - } - - this.updateNow(); - - while (true) { - - // Process pending requests - { - var pending_batch = this.pending.popBatch(); - var pending = pending_batch.iterator(); - - while (pending.next()) |request| { - request.scheduled = false; - switch (request.callback(request)) { - .readable => |readable| { - switch (readable.poll.registerForEpoll(readable.tag, this, .poll_readable, true, readable.fd)) { - .err => |err| { - readable.onError(readable.ctx, err); - }, - .result => { - this.active += 1; - }, - } - }, - .writable => |writable| { - switch (writable.poll.registerForEpoll(writable.tag, this, .poll_writable, true, writable.fd)) { - .err => |err| { - writable.onError(writable.ctx, err); - }, - .result => { - this.active += 1; - }, - } - }, - .close => |close| { - log("close({f}, registered={})", .{ close.fd, close.poll.flags.contains(.registered) }); - // Only remove from the interest list if it was previously registered. - // Otherwise, epoll gets confused. - // This state can happen if polling for readable/writable previously failed. - if (close.poll.flags.contains(.was_ever_registered)) { - close.poll.unregisterWithFd(this.pollfd(), close.fd); - this.active -= 1; - } - close.onDone(close.ctx); - }, - } - } - } - - var events: [256]EventType = undefined; - - const rc = linux.epoll_wait( - this.pollfd().cast(), - &events, - @intCast(events.len), - std.math.maxInt(i32), - ); - - switch (bun.sys.getErrno(rc)) { - .INTR => continue, - .SUCCESS => {}, - else => |e| bun.Output.panic("epoll_wait: {s}", .{@tagName(e)}), - } -``` -The loop drains the entire pending queue first, registering each new fd with epoll (or dispatching its error immediately), then blocks indefinitely in `epoll_wait`. The `comptime !Environment.isLinux` guard at the top deletes this function entirely on non-Linux targets rather than guarding it at runtime. Error handling from the syscall is a three-branch switch: `EINTR` retries, `SUCCESS` continues, everything else is a hard panic — no silent degradation. diff --git a/packs/mitchell-hashimoto.md b/packs/mitchell-hashimoto.md deleted file mode 100644 index 9d75e37..0000000 --- a/packs/mitchell-hashimoto.md +++ /dev/null @@ -1,306 +0,0 @@ -## Code like Mitchell Hashimoto (@mitchellh) · documented tradeoffs - -Hashimoto writes systems code as if the next reader is debugging a production incident at 2 AM with no git blame. Every constant records why the number was chosen and what changed it last. Every acknowledged shortcut is labeled as such: "CS101 version," "based on vibes," "punting it." Error paths don't just propagate — they restore prior state, log loudly when restoration also fails, and fall back to `unreachable` only when the invariant is genuinely impossible to violate. The type system carries the protocol structure: tagged unions for actions, exhaustive switches over every enum variant, and `comptime` checks that reject unsupported platforms before the binary exists. - -### `Parser.zig` — the state machine's core `next()` function: 3-slot return encoding exit/transition/entry -[source](https://github.com/mitchellh/ghostty/blob/49a9181560707936c587ae121656d2d762d27849/src/terminal/Parser.zig) -```zig -pub fn next(self: *Parser, c: u8) [3]?Action { - const effect = table[c][@intFromEnum(self.state)]; - - // log.info("next: {x}", .{c}); - - const next_state = effect.state; - const action = effect.action; - - // After generating the actions, we set our next state. - defer self.state = next_state; - - // When going from one state to another, the actions take place in this order: - // - // 1. exit action from old state - // 2. transition action - // 3. entry action to new state - return [3]?Action{ - // Exit depends on current state - if (self.state == next_state) null else switch (self.state) { - .osc_string => if (self.osc_parser.end(c)) |cmd| - Action{ .osc_dispatch = cmd.* } - else - null, - .dcs_passthrough => Action{ .dcs_unhook = {} }, - .sos_pm_apc_string => Action{ .apc_end = {} }, - else => null, - }, - - self.doAction(action, c), - - // Entry depends on new state - if (self.state == next_state) null else switch (next_state) { - .escape, .dcs_entry, .csi_entry => clear: { - self.clear(); - break :clear null; - }, - .osc_string => osc_string: { - self.osc_parser.reset(); - break :osc_string null; - }, - .dcs_passthrough => dcs_hook: { - // Ignore too many parameters - if (self.params_idx >= MAX_PARAMS) break :dcs_hook null; - // Finalize parameters - if (self.param_acc_idx > 0) { - self.params[self.params_idx] = self.param_acc; - self.params_idx += 1; - } - break :dcs_hook .{ - .dcs_hook = .{ - .intermediates = self.intermediates[0..self.intermediates_idx], - .params = self.params[0..self.params_idx], - .final = c, - }, - }; - }, - .sos_pm_apc_string => Action{ .apc_start = {} }, - else => null, - }, - }; -} -``` -The protocol spec says state transitions fire three ordered actions; the return type is literally `[3]?Action`, so the caller cannot confuse the ordering. `defer` sets the next state after the return is computed, keeping entry/exit symmetry mechanically correct. - -### `Parser.zig` — test shape: named, byte-literal input, destructuring the tagged-union result -[source](https://github.com/mitchellh/ghostty/blob/49a9181560707936c587ae121656d2d762d27849/src/terminal/Parser.zig) -```zig -test "csi: ESC [ H" { - var p = init(); - _ = p.next(0x1B); - _ = p.next(0x5B); - - { - const a = p.next(0x48); - try testing.expect(p.state == .ground); - try testing.expect(a[0] == null); - try testing.expect(a[1].? == .csi_dispatch); - try testing.expect(a[2] == null); - - const d = a[1].?.csi_dispatch; - try testing.expect(d.final == 0x48); - try testing.expect(d.params.len == 0); - } -} - -test "csi: ESC [ 1 ; 4 H" { - var p = init(); - _ = p.next(0x1B); - _ = p.next(0x5B); - _ = p.next(0x31); // 1 - _ = p.next(0x3B); // ; - _ = p.next(0x34); // 4 - - { - const a = p.next(0x48); // H - try testing.expect(p.state == .ground); - try testing.expect(a[0] == null); - try testing.expect(a[1].? == .csi_dispatch); - try testing.expect(a[2] == null); - - const d = a[1].?.csi_dispatch; - try testing.expect(d.final == 'H'); - try testing.expect(d.params.len == 2); - try testing.expectEqual(@as(u16, 1), d.params[0]); - try testing.expectEqual(@as(u16, 4), d.params[1]); - } -} -``` -Each test drives the state machine byte-by-byte with hex literals (comments add the ASCII glyph), then destructures all three slots of the return — checking that the unused two are null is as important as inspecting the live one. Tests are named after the wire sequence, not the method under test. - -### `Screen.zig` — error recovery with `errdefer`: staged rollback with a fallback fallback -[source](https://github.com/mitchellh/ghostty/blob/49a9181560707936c587ae121656d2d762d27849/src/terminal/Screen.zig) -```zig -pub fn setAttribute( - self: *Screen, - attr: sgr.Attribute, -) PageList.IncreaseCapacityError!void { - // If we fail to set our style for any reason, we should revert - // back to the old style. If we fail to do that, we revert back to - // the default style. - const old_style = self.cursor.style; - errdefer { - self.cursor.style = old_style; - self.manualStyleUpdate() catch |err| { - log.warn("setAttribute error restoring old style after failure err={}", .{err}); - self.cursor.style = .{}; - self.manualStyleUpdate() catch unreachable; - }; - } - - switch (attr) { - .unset => { - self.cursor.style = .{}; - }, - - .bold => { - self.cursor.style.flags.bold = true; - }, -``` -The `errdefer` encodes a two-level recovery: restore the saved style, and if that also fails (log it loudly), reset to the default, which must succeed or the invariant is broken. The `catch unreachable` at the end is not laziness — it is a documented claim about what can go wrong. - -### `Screen.zig` — test shape at scale: asserting internal ref-counts, not just observable output -[source](https://github.com/mitchellh/ghostty/blob/49a9181560707936c587ae121656d2d762d27849/src/terminal/Screen.zig) -```zig -test "Screen style basics" { - const testing = std.testing; - const alloc = testing.allocator; - - var s = try Screen.init(alloc, .{ .cols = 80, .rows = 24, .max_scrollback = 1000 }); - defer s.deinit(); - const page = &s.cursor.page_pin.node.data; - try testing.expectEqual(@as(usize, 0), page.styles.count()); - - // Set a new style - try s.setAttribute(.{ .bold = {} }); - try testing.expect(s.cursor.style_id != 0); - try testing.expectEqual(@as(usize, 1), page.styles.count()); - try testing.expect(s.cursor.style.flags.bold); - - // Set another style, we should still only have one since it was unused - try s.setAttribute(.{ .italic = {} }); - try testing.expect(s.cursor.style_id != 0); - try testing.expectEqual(@as(usize, 1), page.styles.count()); - try testing.expect(s.cursor.style.flags.italic); -} -``` -The test reaches into the page's style map to assert that the ref-count is exactly 1, not 2 — proving that replacing a style releases the old entry. Hashimoto tests the memory model, not just the visible state, because the memory model is where bugs hide. - -### `key_encode.zig` — Options struct: each field is its DEC mode number, with a constructor that names what it can't know -[source](https://github.com/mitchellh/ghostty/blob/49a9181560707936c587ae121656d2d762d27849/src/input/key_encode.zig) -```zig -/// Options that affect key encoding behavior. This is a mix of behavior -/// from terminal state as well as application configuration. -pub const Options = struct { - /// Terminal DEC mode 1 - cursor_key_application: bool = false, - - /// Terminal DEC mode 66 - keypad_key_application: bool = false, - - // DEC Backarrow Key Mode (DECBKM) - // See https://vt100.net/dec/ek-vt3xx-tp-002.pdf page 170 - // If `false` (the default), `backspace` emits 0x7f - // If `true`, `backspace` emits 0x08 - backarrow_key_mode: bool = false, - - /// Terminal DEC mode 1035 - ignore_keypad_with_numlock: bool = false, - - /// Terminal DEC mode 1036 - alt_esc_prefix: bool = false, - - /// xterm "modifyOtherKeys mode 2". Details here: - /// https://invisible-island.net/xterm/modified-keys.html - modify_other_keys_state_2: bool = false, - - /// Kitty keyboard protocol flags. - kitty_flags: KittyFlags = .disabled, - - /// Determines whether the "option" key on macOS is treated - /// as "alt" or not. See the Ghostty `macos_option-as-alt` config - /// docs for a more detailed description of why this is needed. - macos_option_as_alt: OptionAsAlt = .false, - - pub const default: Options = .{ - .cursor_key_application = false, - .keypad_key_application = false, - .ignore_keypad_with_numlock = false, - .alt_esc_prefix = false, - .modify_other_keys_state_2 = false, - .kitty_flags = .disabled, - .macos_option_as_alt = .false, - }; - - /// Initialize our options from the terminal state. - /// - /// Note that `macos_option_as_alt` cannot be determined from - /// terminal state so it must be set manually after this call. - pub fn fromTerminal(t: *const Terminal) Options { - return .{ - .alt_esc_prefix = t.modes.get(.alt_esc_prefix), - .cursor_key_application = t.modes.get(.cursor_keys), - .keypad_key_application = t.modes.get(.keypad_keys), - .backarrow_key_mode = t.modes.get(.backarrow_key_mode), - .ignore_keypad_with_numlock = t.modes.get(.ignore_keypad_with_numlock), - .modify_other_keys_state_2 = t.flags.modify_other_keys_2, - .kitty_flags = t.screens.active.kitty_keyboard.current(), - - // These can't be known from the terminal state. - .macos_option_as_alt = .false, - }; - } -}; -``` -Every boolean field cites its spec number and, for less obvious ones, quotes the wire behavior. The `fromTerminal` constructor names in its comment what it deliberately cannot fill in, so callers know exactly which field to set manually — and why the constructor doesn't just accept the whole config. - -### `lru.zig` — admitted "CS101" implementation comment, plus a return type that surfaces eviction to the caller -[source](https://github.com/mitchellh/ghostty/blob/49a9181560707936c587ae121656d2d762d27849/src/datastruct/lru.zig) -```zig -/// Note: This is a really elementary CS101 version of an LRU right now. -/// This is done initially to get something working. Once we have it working, -/// we can benchmark and improve if this ends up being a source of slowness. -pub fn HashMap( - comptime K: type, - comptime V: type, - comptime Context: type, - comptime max_load_percentage: u64, -) type { - return struct { - const Self = @This(); - const Queue = std.DoublyLinkedList; - const Map = std.HashMapUnmanaged( - K, - *Entry, - Context, - max_load_percentage, - ); - - /// Map to maintain our entries. - map: Map, - - /// Queue to maintain LRU order. - queue: Queue, - - /// The capacity of our map. If this capacity is reached, cache - /// misses will begin evicting entries. - capacity: Map.Size, - - const Entry = struct { - data: KV, - node: Queue.Node, - - fn fromNode(node: *Queue.Node) *Entry { - return @fieldParentPtr("node", node); - } - }; - - pub const KV = struct { - key: K, - value: V, - }; - - /// The result of a getOrPut operation. - pub const GetOrPutResult = struct { - /// The entry that was retrieved. If found_existing is false, - /// then this is a pointer to allocated space to store a V. - /// If found_existing is true, the pointer value is valid, but - /// can be overwritten. - value_ptr: *V, - - /// Whether an existing value was found or not. - found_existing: bool, - - /// If another entry had to be evicted to make space for this - /// put operation, then this is the value that was evicted. - evicted: ?KV, - }; -``` -"CS101 version" is the honest label for a hashmap + doubly-linked list — no pretense of sophistication. The `GetOrPutResult` struct then shows the flip side of that honesty: eviction is not hidden behind a silent drop, it is surfaced as a `?KV` so callers can free or log what they lost. diff --git a/packs/rich-harris.md b/packs/rich-harris.md deleted file mode 100644 index 631ead0..0000000 --- a/packs/rich-harris.md +++ /dev/null @@ -1,243 +0,0 @@ -## Code like Rich Harris (@Rich-Harris) · compiler-grade precision - -Rich Harris writes library code with the discipline of a compiler author: every class owns exactly one responsibility, every piece of mutable state is named and tracked explicitly, and invalid input throws immediately with a precise message instead of silently degrading. Data structures are pointer-based and mutations are surgical — relinking a doubly-linked list in a single focused pass with no helper indirection. Boolean flags are packed into bit fields to avoid object allocation and property-access overhead. Small utility functions are given overloaded type signatures that cover every legal call shape, and error codes are string constants declared alphabetically in one place so that `grep` and `switch` always find the canonical definition. - -### `MagicString.js` — constructor: explicit field manifest via `Object.defineProperties` -[source](https://github.com/Rich-Harris/magic-string/blob/410fd4d080d8bf0b5be900c16c8ba11276fd8749/src/MagicString.js) -```js -export default class MagicString { - constructor(string, options = {}) { - const chunk = new Chunk(0, string.length, string); - - Object.defineProperties(this, { - original: { writable: true, value: string }, - outro: { writable: true, value: '' }, - intro: { writable: true, value: '' }, - firstChunk: { writable: true, value: chunk }, - lastChunk: { writable: true, value: chunk }, - lastSearchedChunk: { writable: true, value: chunk }, - byStart: { writable: true, value: {} }, - byEnd: { writable: true, value: {} }, - filename: { writable: true, value: options.filename }, - indentExclusionRanges: { writable: true, value: options.indentExclusionRanges }, - sourcemapLocations: { writable: true, value: new BitSet() }, - storedNames: { writable: true, value: {} }, - indentStr: { writable: true, value: undefined }, - ignoreList: { writable: true, value: options.ignoreList }, - offset: { writable: true, value: options.offset || 0 }, - }); - - if (DEBUG) { - Object.defineProperty(this, 'stats', { value: new Stats() }); - } - - this.byStart[0] = chunk; - this.byEnd[string.length] = chunk; - } -``` -Using `Object.defineProperties` instead of `this.x =` assignments makes every field's writability explicit and prevents accidental enumeration — every property of the object is a deliberate, named decision rather than an incidental assignment. - -### `MagicString.js` — `update`: validate first, then mutate -[source](https://github.com/Rich-Harris/magic-string/blob/410fd4d080d8bf0b5be900c16c8ba11276fd8749/src/MagicString.js) -```js - update(start, end, content, options) { - start = start + this.offset; - end = end + this.offset; - - if (typeof content !== 'string') throw new TypeError('replacement content must be a string'); - - if (this.original.length !== 0) { - while (start < 0) start += this.original.length; - while (end < 0) end += this.original.length; - } - - if (end > this.original.length) throw new Error('end is out of bounds'); - if (start === end) - throw new Error( - 'Cannot overwrite a zero-length range – use appendLeft or prependRight instead', - ); - - if (DEBUG) this.stats.time('overwrite'); - - this._split(start); - this._split(end); -``` -Every guard fires before any state is touched: type check, Python-style negative-index normalization, out-of-bounds check, zero-length check — each with an actionable message naming the correct alternative. Only after all guards pass does structural mutation begin. - -### `blank.ts` — frozen sentinels replace re-allocation -[source](https://github.com/rollup/rollup/blob/5e0066d92defee0097f10fb814e63f60b2a7b612/src/utils/blank.ts) -```ts -export const BLANK: Record = Object.freeze(Object.create(null)); -export const EMPTY_OBJECT = Object.freeze({}); -export const EMPTY_ARRAY = Object.freeze([]); -export const EMPTY_SET = Object.freeze( - new (class extends Set { - add(): never { - throw new Error('Cannot add to empty set'); - } - })() -); -``` -Constants that could be `{}` or `[]` are instead named, frozen, and (for `BLANK`) proto-less so they are safe as `Record` without `hasOwnProperty` guards. `EMPTY_SET` goes further: it subclasses `Set` to make mutation a thrown error, catching callers that accidentally write to a sentinel they should only read from. - -### `BitFlags.ts` — boolean state packed into a `const enum` bit field -[source](https://github.com/rollup/rollup/blob/5e0066d92defee0097f10fb814e63f60b2a7b612/src/ast/nodes/shared/BitFlags.ts) -```ts -export const enum Flag { - included = 1 << 0, - deoptimized = 1 << 1, - tdzAccessDefined = 1 << 2, - tdzAccess = 1 << 3, - assignmentDeoptimized = 1 << 4, - bound = 1 << 5, - isUndefined = 1 << 6, - optional = 1 << 7, - async = 1 << 8, - deoptimizedReturn = 1 << 9, - computed = 1 << 10, - hasLostTrack = 1 << 11, - hasUnknownDeoptimizedInteger = 1 << 12, - hasUnknownDeoptimizedProperty = 1 << 13, - directlyIncluded = 1 << 14, - deoptimizeBody = 1 << 15, - isBranchResolutionAnalysed = 1 << 16, - await = 1 << 17, - method = 1 << 18, - shorthand = 1 << 19, - tail = 1 << 20, - prefix = 1 << 21, - generator = 1 << 22, - expression = 1 << 23, - destructuringDeoptimized = 1 << 24, - hasDeoptimizedCache = 1 << 25, - hasEffects = 1 << 26, - checkedForWarnings = 1 << 27, - shouldIncludeDynamicAttributes = 1 << 28 -} - -export function isFlagSet(flags: number, flag: Flag): boolean { - return (flags & flag) !== 0; -} - -export function setFlag(flags: number, flag: Flag, value: boolean): number { - return (flags & ~flag) | (-value & flag); -} -``` -Twenty-nine boolean properties of AST nodes are packed into a single integer rather than stored as object fields, reducing allocation cost across millions of nodes. The two helper functions are the only gateway — every get and set in the codebase goes through `isFlagSet`/`setFlag`, keeping the bit arithmetic in one place. - -### `Queue.ts` — minimal class with one private loop -[source](https://github.com/rollup/rollup/blob/5e0066d92defee0097f10fb814e63f60b2a7b612/src/utils/Queue.ts) -```ts -type Task = () => Promise; - -interface QueueItem { - reject: (reason?: unknown) => void; - resolve: (value: any) => void; - task: Task; -} - -export default class Queue { - private readonly queue: QueueItem[] = []; - private workerCount = 0; - - constructor(private maxParallel: number) {} - - run(task: Task): Promise { - return new Promise((resolve, reject) => { - this.queue.push({ reject, resolve, task }); - this.work(); - }); - } - - private async work(): Promise { - if (this.workerCount >= this.maxParallel) return; - this.workerCount++; - - let entry: QueueItem | undefined; - while ((entry = this.queue.shift())) { - const { reject, resolve, task } = entry; - - try { - const result = await task(); - resolve(result); - } catch (error) { - reject(error); - } - } - - this.workerCount--; - } -} -``` -Forty lines, two fields, two methods — nothing more. `run` is the only public surface; `work` is entirely private. The `while` loop drains however many items are queued without recursion, and `workerCount` guards against spawning more concurrent workers than `maxParallel` without any external dependency or complex locking. - -### `logs.ts` — error handling: structured log objects and a single throw gateway -[source](https://github.com/rollup/rollup/blob/5e0066d92defee0097f10fb814e63f60b2a7b612/src/utils/logs.ts) -```ts -export function error(base: Error | RollupLog): never { - throw base instanceof Error ? base : getRollupError(base); -} - -export function getRollupError(base: RollupLog): Error & RollupLog { - augmentLogMessage(base); - const errorInstance = Object.assign(new Error(base.message), base); - Object.defineProperty(errorInstance, 'name', { - value: 'RollupError', - writable: true - }); - return errorInstance; -} - -export function augmentCodeLocation( - properties: RollupLog, - pos: number | { column: number; line: number }, - source: string, - id: string -): void { - if (typeof pos === 'object') { - const { line, column } = pos; - properties.loc = { column, file: id, line }; - } else { - properties.pos = pos; - const location = locate(source, pos, { offsetLine: 1 }); - if (!location) { - return; - } - const { line, column } = location; - properties.loc = { column, file: id, line }; - } - - if (properties.frame === undefined) { - const { line, column } = properties.loc; - properties.frame = getCodeFrame(source, line, column); - } -} -``` -`error()` is the single throw site in the entire codebase — everything else builds a `RollupLog` plain object and passes it here. `getRollupError` uses `Object.assign` + `Object.defineProperty` to attach the log's structured fields to a real `Error` instance (so stack traces work) while overriding `name` to `'RollupError'` without making it enumerable. Error construction and throwing are two separate, testable operations. - -### `MagicString.test.js` — test shape: flat `describe`/`it`, assert-only, one concept per case -[source](https://github.com/Rich-Harris/magic-string/blob/410fd4d080d8bf0b5be900c16c8ba11276fd8749/test/MagicString.test.js) -```js - describe('append', () => { - it('should append content', () => { - const s = new MagicString('abcdefghijkl'); - - s.append('xyz'); - assert.equal(s.toString(), 'abcdefghijklxyz'); - - s.append('xyz'); - assert.equal(s.toString(), 'abcdefghijklxyzxyz'); - }); - - it('should return this', () => { - const s = new MagicString('abcdefghijkl'); - assert.strictEqual(s.append('xyz'), s); - }); - - it('should throw when given non-string content', () => { - const s = new MagicString(''); - assert.throws(() => s.append([]), TypeError); - }); - }); -``` -Tests are organized by method name, not by scenario type. Each `it` proves exactly one thing — idempotent accumulation, fluent return, type rejection — with no setup helpers, no `beforeEach`, and no mocking. Node's built-in `assert` is used directly; the test file imports nothing more than the class under test and the assert module. diff --git a/packs/simon-willison.md b/packs/simon-willison.md deleted file mode 100644 index e4987c9..0000000 --- a/packs/simon-willison.md +++ /dev/null @@ -1,372 +0,0 @@ -## Code like Simon Willison (@simonw) · one concept per file - -Every file has exactly one job and says so in its name. Pure functions registered via `@hookimpl` replace classes wherever possible; where a class is necessary, it is small and data-shaped (a dataclass or a namedtuple). Type logic is expressed as explicit set arithmetic on Python's own type objects rather than string tags or nested conditionals, and error handling routes by exception type first, then by response format — never by catching `Exception` broadly. The result is code you can read top-to-bottom without a map. - -### `default_magic_parameters.py` — one file, one concept: SQL magic parameters as plain functions -[source](https://github.com/simonw/datasette/blob/dfd5b95ec8adc425b683df22148cb1c14bb01128/datasette/default_magic_parameters.py) -```python -from datasette import hookimpl -import datetime -import os -import time - - -def header(key, request): - key = key.replace("_", "-").encode("utf-8") - headers_dict = dict(request.scope["headers"]) - return headers_dict.get(key, b"").decode("utf-8") - - -def actor(key, request): - if request.actor is None: - raise KeyError - return request.actor[key] - - -def cookie(key, request): - return request.cookies[key] - - -def now(key, request): - if key == "epoch": - return int(time.time()) - elif key == "date_utc": - return datetime.datetime.now(datetime.timezone.utc).date().isoformat() - elif key == "datetime_utc": - return ( - datetime.datetime.now(datetime.timezone.utc).strftime(r"%Y-%m-%dT%H:%M:%S") - + "Z" - ) - else: - raise KeyError - - -def random(key, request): - if key.startswith("chars_") and key.split("chars_")[-1].isdigit(): - num_chars = int(key.split("chars_")[-1]) - if num_chars % 2 == 1: - urandom_len = (num_chars + 1) / 2 - else: - urandom_len = num_chars / 2 - return os.urandom(int(urandom_len)).hex()[:num_chars] - else: - raise KeyError - - -@hookimpl -def register_magic_parameters(): - return [ - ("header", header), - ("actor", actor), - ("cookie", cookie), - ("now", now), - ("random", random), - ] -``` -The entire file is one bounded concept: five pure functions, each named after the SQL magic parameter it handles, collected at the bottom by one `@hookimpl`. There is no class, no shared state — adding a new parameter means adding one function and one tuple entry. - -### `handle_exception.py` — error handling: classify by exception type, route by content-type -[source](https://github.com/simonw/datasette/blob/dfd5b95ec8adc425b683df22148cb1c14bb01128/datasette/handle_exception.py) -```python -@hookimpl(trylast=True) -def handle_exception(datasette, request, exception): - async def inner(): - if datasette.pdb: - pdb.post_mortem(exception.__traceback__) - - if rich is not None: - rich.get_console().print_exception(show_locals=True) - - title = None - if isinstance(exception, Base400): - status = exception.status - info = {} - message = exception.args[0] - elif isinstance(exception, DatasetteError): - status = exception.status - info = exception.error_dict - message = exception.message - if exception.message_is_html: - message = Markup(message) - title = exception.title - else: - status = 500 - info = {} - message = str(exception) - traceback.print_exc() - templates = [f"{status}.html", "error.html"] - info.update( - { - "ok": False, - "error": message, - "status": status, - "title": title, - } - ) - headers = {} - if datasette.cors: - add_cors_headers(headers) - if request.path.split("?")[0].endswith(".json"): - return Response.json(info, status=status, headers=headers) - else: - environment = datasette.get_jinja_environment(request) - template = environment.select_template(templates) - return Response.html( - await template.render_async( - dict( - info, - urls=datasette.urls, - app_css_hash=datasette.app_css_hash(), - menu_links=lambda: [], - ) - ), - status=status, - headers=headers, - ) - - return inner -``` -Error handling is one `isinstance` chain that maps each exception class to a status code, then a single content-type branch at the bottom routes JSON vs. HTML — no try/except swallowing, no generic catch-all message. The whole handler is a `@hookimpl(trylast=True)` so other plugins can intercept first. - -### `events.py` — typed dataclass events: abstract base, concrete subclasses, no string tags -[source](https://github.com/simonw/datasette/blob/dfd5b95ec8adc425b683df22148cb1c14bb01128/datasette/events.py) -```python -@dataclass -class Event(ABC): - @abstractproperty - def name(self): - pass - - created: datetime = field( - init=False, default_factory=lambda: datetime.now(timezone.utc) - ) - actor: dict | None - - def properties(self): - properties = asdict(self) - properties.pop("actor", None) - properties.pop("created", None) - return properties - - -@dataclass -class LoginEvent(Event): - """ - Event name: ``login`` - - A user (represented by ``event.actor``) has logged in. - """ - - name = "login" -``` -Events are plain dataclasses that inherit a shared `created` timestamp and a `properties()` serialiser; each subclass is just a class-level `name` constant plus typed fields. No string event buses, no dicts — the type itself is the contract. - -### `utils.py` — type inference as explicit set arithmetic, not nested ifs -[source](https://github.com/simonw/sqlite-utils/blob/8f0c06e1889513ed0f01cb57783ddf07c442d4be/sqlite_utils/utils.py) -```python -def types_for_column_types( - all_column_types: Dict[str, Set[type]], -) -> Dict[str, type]: - column_types: Dict[str, type] = {} - for key, types in all_column_types.items(): - # Ignore null values if at least one other type present: - if len(types) > 1: - types.discard(None.__class__) - t: type - if {None.__class__} == types: - t = str - elif len(types) == 1: - t = list(types)[0] - # But if it's a subclass of list / tuple / dict, use str - # instead as we will be storing it as JSON in the table - for superclass in (list, tuple, dict): - if issubclass(t, superclass): - t = str - elif {int, bool}.issuperset(types): - t = int - elif {int, float, bool}.issuperset(types): - t = float - elif {bytes, str}.issuperset(types): - t = bytes - else: - t = str - column_types[key] = t - return column_types -``` -The full type-coercion lattice (null → str, bool ⊂ int ⊂ float, containers → JSON str) is expressed as set operations on Python's own type objects — no string tags, no enums, no switch tables. Each branch is one readable predicate: `{int, bool}.issuperset(types)`. - -### `filters.py` — parameterised SQL as data: the TemplatedFilter class hierarchy -[source](https://github.com/simonw/datasette/blob/dfd5b95ec8adc425b683df22148cb1c14bb01128/datasette/filters.py) -```python -class TemplatedFilter(Filter): - def __init__( - self, - key, - display, - sql_template, - human_template, - format="{}", - numeric=False, - no_argument=False, - ): - self.key = key - self.display = display - self.sql_template = sql_template - self.human_template = human_template - self.format = format - self.numeric = numeric - self.no_argument = no_argument - - def where_clause(self, table, column, value, param_counter): - converted = self.format.format(value) - if self.numeric and converted.isdigit(): - converted = int(converted) - if self.no_argument: - kwargs = {"c": column} - converted = None - else: - kwargs = {"c": column, "p": f"p{param_counter}", "t": table} - return self.sql_template.format(**kwargs), converted - - def human_clause(self, column, value): - if callable(self.human_template): - template = self.human_template(column, value) - else: - template = self.human_template - if self.no_argument: - return template.format(c=column) - else: - return template.format(c=column, v=value) - - -class InFilter(Filter): - key = "in" - display = "in" - - def split_value(self, value): - if value.startswith("["): - return json.loads(value) - else: - return [v.strip() for v in value.split(",")] - - def where_clause(self, table, column, value, param_counter): - values = self.split_value(value) - params = [f":p{param_counter + i}" for i in range(len(values))] - sql = f"{escape_sqlite(column)} in ({', '.join(params)})" - return sql, values - - def human_clause(self, column, value): - return f"{column} in {json.dumps(self.split_value(value))}" -``` -Each filter type is a small class that carries its SQL template and its human-readable label together; `TemplatedFilter` lets you instantiate a filter from data (key, display string, SQL string, human string) rather than writing a subclass. Every `where_clause` returns `(sql, converted_value)` — the same shape, no side effects. - -### `db.py` — the dual-mode decorator: `register_function` with and without arguments -[source](https://github.com/simonw/sqlite-utils/blob/8f0c06e1889513ed0f01cb57783ddf07c442d4be/sqlite_utils/db.py) -```python - def register_function( - self, - fn: Optional[Callable] = None, - deterministic: bool = False, - replace: bool = False, - name: Optional[str] = None, - ) -> Optional[Callable[[Callable], Callable]]: - """ - ``fn`` will be made available as a function within SQL, with the same name and number - of arguments. Can be used as a decorator:: - - @db.register_function - def upper(value): - return str(value).upper() - - The decorator can take arguments:: - - @db.register_function(deterministic=True, replace=True) - def upper(value): - return str(value).upper() - - See :ref:`python_api_register_function`. - - :param fn: Function to register - :param deterministic: set ``True`` for functions that always returns the same output for a given input - :param replace: set ``True`` to replace an existing function with the same name - otherwise throw an error - :param name: name of the SQLite function - if not specified, the Python function name will be used - """ - - def register(fn: Callable) -> Callable: - fn_name = name or fn.__name__ # type: ignore - arity = len(inspect.signature(fn).parameters) - if not replace and (fn_name, arity) in self._registered_functions: - return fn - kwargs: Dict[str, bool] = {} - registered = False - if deterministic: - # Try this, but fall back if sqlite3.NotSupportedError - try: - self.conn.create_function( - fn_name, arity, fn, **dict(kwargs, deterministic=True) - ) - registered = True - except sqlite3.NotSupportedError: - pass - if not registered: - self.conn.create_function(fn_name, arity, fn, **kwargs) - self._registered_functions.add((fn_name, arity)) - return fn - - if fn is None: - return register - else: - register(fn) - return None -``` -The classic optional-argument decorator idiom: if `fn is None` the caller passed keyword args, so return the inner `register` closure; otherwise call it immediately. Arity is detected via `inspect.signature` so users never declare it; `deterministic=True` is tried first and silently degrades on old SQLite — one specific `except`, never bare `except Exception`. - -### `test_fts.py` — test shape: real fixture data, real queries, assert on exact output -[source](https://github.com/simonw/sqlite-utils/blob/8f0c06e1889513ed0f01cb57783ddf07c442d4be/tests/test_fts.py) -```python -search_records = [ - { - "text": "tanuki are running tricksters", - "country": "Japan", - "not_searchable": "foo", - }, - { - "text": "racoons are biting trash pandas", - "country": "USA", - "not_searchable": "bar", - }, -] - - -def test_enable_fts(fresh_db): - table = fresh_db["searchable"] - table.insert_all(search_records) - assert ["searchable"] == fresh_db.table_names() - table.enable_fts(["text", "country"], fts_version="FTS4") - assert [ - "searchable", - "searchable_fts", - "searchable_fts_segments", - "searchable_fts_segdir", - "searchable_fts_docsize", - "searchable_fts_stat", - ] == fresh_db.table_names() - assert [ - { - "rowid": 1, - "text": "tanuki are running tricksters", - "country": "Japan", - "not_searchable": "foo", - } - ] == list(table.search("tanuki")) - assert [ - { - "rowid": 2, - "text": "racoons are biting trash pandas", - "country": "USA", - "not_searchable": "bar", - } - ] == list(table.search("usa")) - assert [] == list(table.search("bar")) -``` -Tests use a module-level fixture of real-shaped dicts, insert them into a real in-memory SQLite database via `fresh_db`, then assert on the complete returned dict — no mocks, no `assert result is not None`, no counting. Each assertion verifies the full output including fields that should not be searched (`not_searchable`), so the test doubles as a spec. diff --git a/packs/sindre-sorhus.md b/packs/sindre-sorhus.md deleted file mode 100644 index 0a339ca..0000000 --- a/packs/sindre-sorhus.md +++ /dev/null @@ -1,321 +0,0 @@ -## Code like Sindre Sorhus (@sindresorhus) · one file, one job - -Radical minimalism: each module does exactly one thing and is small enough to read in five minutes. State lives in closures, never in classes; the public surface is a single callable decorated with `Object.defineProperties` so introspection properties stay non-enumerable and non-leaking. Options are validated and normalized in one upfront pass so every downstream function receives a fully-resolved struct. Error paths enumerate every failure reason via priority-ordered early returns — one `if`, one `return`, no inheritance hierarchy. Comments explain the non-obvious *why* (the async resolve-with-promise trick, the `__proto__: null` pollution guard), not the what. - -### `index.js` — module shape: closure state, single callable, `Object.defineProperties` for the public surface -[source](https://github.com/sindresorhus/p-limit/blob/42599ebbbb1228a5bdab381fcf8f4ac20eb8d551/index.js) -```js -export default function pLimit(concurrency) { - let rejectOnClear = false; - - if (typeof concurrency === 'object') { - ({concurrency, rejectOnClear = false} = concurrency); - } - - validateConcurrency(concurrency); - - if (typeof rejectOnClear !== 'boolean') { - throw new TypeError('Expected `rejectOnClear` to be a boolean'); - } - - const queue = new Queue(); - let activeCount = 0; -``` - -Input is unpacked and fully validated before any internal state is allocated. The options object shorthand (`typeof concurrency === 'object'`) lets a single function accept both the legacy scalar form and the new named-options form with no overloaded signatures. - -### `index.js` — the public API surface: `Object.defineProperties` over a plain callable, no class -[source](https://github.com/sindresorhus/p-limit/blob/42599ebbbb1228a5bdab381fcf8f4ac20eb8d551/index.js) -```js - Object.defineProperties(generator, { - activeCount: { - get: () => activeCount, - }, - pendingCount: { - get: () => queue.size, - }, - clearQueue: { - value() { - if (!rejectOnClear) { - queue.clear(); - return; - } - - const abortError = AbortSignal.abort().reason; - - while (queue.size > 0) { - queue.dequeue().reject(abortError); - } - }, - }, - concurrency: { - get: () => concurrency, - - set(newConcurrency) { - validateConcurrency(newConcurrency); - concurrency = newConcurrency; - - queueMicrotask(() => { - // eslint-disable-next-line no-unmodified-loop-condition - while (activeCount < concurrency && queue.size > 0) { - resumeNext(); - } - }); - }, - }, - map: { - async value(iterable, function_) { - const promises = Array.from(iterable, (value, index) => this(function_, value, index)); - return Promise.all(promises); - }, - }, - }); - - return generator; -} -``` - -`generator` is a plain function — callers invoke it like `limit(fn)` — but `Object.defineProperties` bolts on read-only getters and methods. Internals (`queue`, `activeCount`) stay in the closure and never appear on the returned object. The `concurrency` setter re-drains via `queueMicrotask` so dynamic resizing is non-blocking. - -### `test.js` — test shape: real timing with `timeSpan` + `inRange`, no mocks -[source](https://github.com/sindresorhus/p-limit/blob/42599ebbbb1228a5bdab381fcf8f4ac20eb8d551/test.js) -```js -test('concurrency: 1', async t => { - const input = [ - [10, 300], - [20, 200], - [30, 100], - ]; - - const end = timeSpan(); - const limit = pLimit(1); - - const mapper = ([value, ms]) => limit(async () => { - await delay(ms); - return value; - }); - - t.deepEqual(await Promise.all(input.map(x => mapper(x))), [10, 20, 30]); - t.true(inRange(end(), {start: 590, end: 650})); -}); - -test('concurrency: 4', async t => { - const concurrency = 5; - let running = 0; - - const limit = pLimit(concurrency); - - const input = Array.from({length: 100}, () => limit(async () => { - running++; - t.true(running <= concurrency); - await delay(randomInt(30, 200)); - running--; - })); - - await Promise.all(input); -}); -``` - -Tests verify observable behavior at the boundary — wall-clock ordering and live concurrency counts — not internal state. `timeSpan` + `inRange` assert timing without brittle exact matches. There are no mocks: the library runs against real async operations. - -### `lib/arguments/options.js` — normalize-before-use: one upfront sequential pass, then a clean struct downstream -[source](https://github.com/sindresorhus/execa/blob/f3a2e8481a1e9138de3895827895c834078b9456/lib/arguments/options.js) -```js -// Normalize the options object, and sometimes also the file paths and arguments. -// Applies default values, validate allowed options, normalize them. -export const normalizeOptions = (filePath, rawArguments, rawOptions) => { - // Prevent prototype pollution by copying only own properties to a null-prototype object - const sanitizedOptions = {__proto__: null, ...rawOptions}; - sanitizedOptions.cwd = normalizeCwd(sanitizedOptions.cwd); - const [processedFile, processedArguments, processedOptions] = handleNodeOption(filePath, rawArguments, sanitizedOptions); - - const {command: file, args: commandArguments, options: initialOptions} = crossSpawn._parse(processedFile, processedArguments, processedOptions); - - const fdOptions = normalizeFdSpecificOptions(initialOptions); - const options = addDefaultOptions(fdOptions); - validateTimeout(options); - validateEncoding(options); - validateIpcInputOption(options); - validateCancelSignal(options); - validateGracefulCancel(options); - options.shell = normalizeFileUrl(options.shell); - options.env = getEnv(options); - options.killSignal = normalizeKillSignal(options.killSignal); - options.forceKillAfterDelay = normalizeForceKillAfterDelay(options.forceKillAfterDelay); - options.lines = options.lines.map((lines, fdNumber) => lines && !BINARY_ENCODINGS.has(options.encoding) && options.buffer[fdNumber]); - - if (process.platform === 'win32' && path.basename(file, '.exe') === 'cmd') { - // #116 - commandArguments.unshift('/q'); - } - - return {file, commandArguments, options}; -}; -``` - -Every validate/normalize call runs in a fixed sequence before the subprocess is ever spawned. Callers downstream receive a fully-resolved struct and never check option validity themselves. The `{__proto__: null, ...rawOptions}` spread kills prototype pollution in one line rather than a guard library. - -### `lib/return/message.js` — error reason enumeration: priority-ordered early returns, no subclasses -[source](https://github.com/sindresorhus/execa/blob/f3a2e8481a1e9138de3895827895c834078b9456/lib/return/message.js) -```js -const getErrorPrefix = ({ - originalError, - timedOut, - timeout, - isMaxBuffer, - maxBuffer, - errorCode, - signal, - signalDescription, - exitCode, - isCanceled, - isGracefullyCanceled, - isForcefullyTerminated, - forceKillAfterDelay, - killSignal, -}) => { - const forcefulSuffix = getForcefulSuffix(isForcefullyTerminated, forceKillAfterDelay); - - if (timedOut) { - return `Command timed out after ${timeout} milliseconds${forcefulSuffix}`; - } - - if (isGracefullyCanceled) { - if (signal === undefined) { - return `Command was gracefully canceled with exit code ${exitCode}`; - } - - return isForcefullyTerminated - ? `Command was gracefully canceled${forcefulSuffix}` - : `Command was gracefully canceled with ${signal} (${signalDescription})`; - } - - if (isCanceled) { - return `Command was canceled${forcefulSuffix}`; - } - - if (isMaxBuffer) { - return `${getMaxBufferMessage(originalError, maxBuffer)}${forcefulSuffix}`; - } - - if (errorCode !== undefined) { - return `Command failed with ${errorCode}${forcefulSuffix}`; - } - - if (isForcefullyTerminated) { - return `Command was killed with ${killSignal} (${getSignalDescription(killSignal)})${forcefulSuffix}`; - } - - if (signal !== undefined) { - return `Command was killed with ${signal} (${signalDescription})`; - } - - if (exitCode !== undefined) { - return `Command failed with exit code ${exitCode}`; - } - - return 'Command failed'; -}; -``` - -Every possible termination reason maps to exactly one human-readable string via priority-ordered early returns — no switch, no error subclasses, no inheritance hierarchy. The function is a pure input→string transform; adding a new reason means adding one `if` block with one `return`. - -### `lib/return/final-error.js` — error class wiring: `Object.defineProperty` for non-enumerable name, cross-realm identity via Symbol -[source](https://github.com/sindresorhus/execa/blob/f3a2e8481a1e9138de3895827895c834078b9456/lib/return/final-error.js) -```js -// When the subprocess fails, this is the error instance being returned. -// If another error instance is being thrown, it is kept as `error.cause`. -export const getFinalError = (originalError, message, isSync) => { - const ErrorClass = isSync ? ExecaSyncError : ExecaError; - const options = originalError instanceof DiscardedError ? {} : {cause: originalError}; - return new ErrorClass(message, options); -}; - -// Indicates that the error is used only to interrupt control flow, but not in the return value -export class DiscardedError extends Error {} - -// Proper way to set `error.name`: it should be inherited and non-enumerable -const setErrorName = (ErrorClass, value) => { - Object.defineProperty(ErrorClass.prototype, 'name', { - value, - writable: true, - enumerable: false, - configurable: true, - }); - Object.defineProperty(ErrorClass.prototype, execaErrorSymbol, { - value: true, - writable: false, - enumerable: false, - configurable: false, - }); -}; - -// Unlike `instanceof`, this works across realms -export const isExecaError = error => isErrorInstance(error) && execaErrorSymbol in error; - -const execaErrorSymbol = Symbol('isExecaError'); - -export const isErrorInstance = value => Object.prototype.toString.call(value) === '[object Error]'; - -// We use two different Error classes for async/sync methods since they have slightly different shape and types -export class ExecaError extends Error {} -setErrorName(ExecaError, ExecaError.name); - -export class ExecaSyncError extends Error {} -setErrorName(ExecaSyncError, ExecaSyncError.name); -``` - -`error.name` is set via `Object.defineProperty` (non-enumerable, inherited from the prototype) rather than a class field — the comment explains why. Cross-realm identity uses a Symbol-keyed property instead of `instanceof` since `instanceof` breaks across VM contexts. A `DiscardedError` sentinel class separates control-flow errors from user-facing ones so they never bleed into return values. - -### `lib/verbose/default.js` — data-table dispatch: two object literals replace a switch across message types -[source](https://github.com/sindresorhus/execa/blob/f3a2e8481a1e9138de3895827895c834078b9456/lib/verbose/default.js) -```js -// Default when `verbose` is not a function -export const defaultVerboseFunction = ({ - type, - message, - timestamp, - piped, - commandId, - result: {failed = false} = {}, - options: {reject = true}, -}) => { - const timestampString = serializeTimestamp(timestamp); - const icon = ICONS[type]({failed, reject, piped}); - const color = COLORS[type]({reject}); - return `${gray(`[${timestampString}]`)} ${gray(`[${commandId}]`)} ${color(icon)} ${color(message)}`; -}; - -// Prepending the timestamp allows debugging the slow paths of a subprocess -const serializeTimestamp = timestamp => `${padField(timestamp.getHours(), 2)}:${padField(timestamp.getMinutes(), 2)}:${padField(timestamp.getSeconds(), 2)}.${padField(timestamp.getMilliseconds(), 3)}`; - -const padField = (field, padding) => String(field).padStart(padding, '0'); - -const getFinalIcon = ({failed, reject}) => { - if (!failed) { - return figures.tick; - } - - return reject ? figures.cross : figures.warning; -}; - -const ICONS = { - command: ({piped}) => piped ? '|' : '$', - output: () => ' ', - ipc: () => '*', - error: getFinalIcon, - duration: getFinalIcon, -}; - -const identity = string => string; - -const COLORS = { - command: () => bold, - output: () => identity, - ipc: () => identity, - error: ({reject}) => reject ? redBright : yellowBright, - duration: () => gray, -}; -``` - -`ICONS` and `COLORS` are plain object literals keyed by message type — functions that receive only what they need. There is no switch, no if-chain per type. Adding a new message type means adding one key to each table; the dispatch in `defaultVerboseFunction` never changes. Destructuring with defaults (`result: {failed = false} = {}`) handles the absent-result case inline. diff --git a/packs/tanner-linsley.md b/packs/tanner-linsley.md deleted file mode 100644 index 932c69d..0000000 --- a/packs/tanner-linsley.md +++ /dev/null @@ -1,344 +0,0 @@ -## Code like Tanner Linsley (@tannerlinsley) · types that forbid bad states - -Every observable quantity that can only be in one of several mutually-exclusive states is modelled as a discriminated union, not a bag of optional booleans. Behaviour is encapsulated in small, focused classes built on a generic `Subscribable` base, with private `#fields` hiding mutable internals and protected hook methods (`onSubscribe`/`onUnsubscribe`) for subclasses to override. Side-effectful logic is extracted into factory functions that return plain objects of closures — no classes, no `this` — making the internals easy to test and the public surface explicit. Algorithms that could silently grow allocations (structural sharing, key hashing, partial-match traversal) are written as tight imperative loops with early exits. Tests verify the exact sequence of side-effects, not just final state, so the notification contract is part of the spec. - -### `subscribable.ts` — the base-class template: generic listener set, closure-returning subscribe, hook methods for subclasses -[source](https://github.com/TanStack/query/blob/0bed37a91efa1b6e84b192ca3629d6e0c6cfcb73/packages/query-core/src/subscribable.ts) -```ts -export class Subscribable { - protected listeners = new Set() - - constructor() { - this.subscribe = this.subscribe.bind(this) - } - - subscribe(listener: TListener): () => void { - this.listeners.add(listener) - - this.onSubscribe() - - return () => { - this.listeners.delete(listener) - this.onUnsubscribe() - } - } - - hasListeners(): boolean { - return this.listeners.size > 0 - } - - protected onSubscribe(): void { - // Do nothing - } - - protected onUnsubscribe(): void { - // Do nothing - } -} -``` -Every observer and cache in the library extends this single class. `subscribe` returns the unsubscribe closure directly — no separate `unsubscribe` method — and the protected `onSubscribe`/`onUnsubscribe` hooks let subclasses react to the first/last listener without touching the listener-set bookkeeping. - -### `query.ts` — reducer inside a private method: exhaustive switch, each case returns a new state object -[source](https://github.com/TanStack/query/blob/0bed37a91efa1b6e84b192ca3629d6e0c6cfcb73/packages/query-core/src/query.ts) -```ts - #dispatch(action: Action): void { - const reducer = ( - state: QueryState, - ): QueryState => { - switch (action.type) { - case 'failed': - return { - ...state, - fetchFailureCount: action.failureCount, - fetchFailureReason: action.error, - } - case 'pause': - return { - ...state, - fetchStatus: 'paused', - } - case 'continue': - return { - ...state, - fetchStatus: 'fetching', - } - case 'fetch': - return { - ...state, - ...fetchState(state.data, this.options), - fetchMeta: action.meta ?? null, - } - case 'success': - const newState = { - ...state, - ...successState(action.data, action.dataUpdatedAt), - dataUpdateCount: state.dataUpdateCount + 1, - ...(!action.manual && { - fetchStatus: 'idle' as const, - fetchFailureCount: 0, - fetchFailureReason: null, - }), - } - // If fetching ends successfully, we don't need revertState as a fallback anymore. - // For manual updates, capture the state to revert to it in case of a cancellation. - this.#revertState = action.manual ? newState : undefined - - return newState - case 'error': - const error = action.error - return { - ...state, - error, - errorUpdateCount: state.errorUpdateCount + 1, - errorUpdatedAt: Date.now(), - fetchFailureCount: state.fetchFailureCount + 1, - fetchFailureReason: error, - fetchStatus: 'idle', - status: 'error', - // flag existing data as invalidated if we get a background error - // note that "no data" always means stale so we can set unconditionally here - isInvalidated: true, - } - case 'invalidate': - return { - ...state, - isInvalidated: true, - } - case 'setState': - return { - ...state, - ...action.state, - } - } - } -``` -State transitions live in a private `#dispatch` that defines its reducer inline and calls it immediately. Each `case` returns a spread of the previous state with only the fields relevant to that transition — no shared mutable bag, no boolean reset ceremony. The TypeScript exhaustive switch means any new `Action` variant that lacks a `case` is a compile error. - -### `retryer.ts` — factory-function pattern: private closure vars, small named inner functions, plain-object return -[source](https://github.com/TanStack/query/blob/0bed37a91efa1b6e84b192ca3629d6e0c6cfcb73/packages/query-core/src/retryer.ts) -```ts -export function createRetryer( - config: RetryerConfig, -): Retryer { - let isRetryCancelled = false - let failureCount = 0 - let continueFn: ((value?: unknown) => void) | undefined - - const thenable = pendingThenable() - - const isResolved = () => - (thenable.status as Thenable['status']) !== 'pending' - - const cancel = (cancelOptions?: CancelOptions): void => { - if (!isResolved()) { - const error = new CancelledError(cancelOptions) as TError - reject(error) - - config.onCancel?.(error) - } - } - const cancelRetry = () => { - isRetryCancelled = true - } - - const continueRetry = () => { - isRetryCancelled = false - } - - const canContinue = () => - focusManager.isFocused() && - (config.networkMode === 'always' || onlineManager.isOnline()) && - config.canRun() - - const canStart = () => canFetch(config.networkMode) && config.canRun() - - const resolve = (value: any) => { - if (!isResolved()) { - continueFn?.() - thenable.resolve(value) - } - } - - const reject = (value: any) => { - if (!isResolved()) { - continueFn?.() - thenable.reject(value) - } - } -``` -No class — just a factory that closes over `failureCount`, `isRetryCancelled`, and `continueFn`, then builds each capability as a named `const` arrow. Guard-and-early-return is the universal shape: `if (!isResolved()) { ... }` before every state mutation. The public `Retryer` interface is returned as a plain object at the end. - -### `utils.ts` — structural-sharing algorithm: imperative loop with reference-equality fast path -[source](https://github.com/TanStack/query/blob/0bed37a91efa1b6e84b192ca3629d6e0c6cfcb73/packages/query-core/src/utils.ts) -```ts -export function replaceEqualDeep(a: any, b: any, depth = 0): any { - if (a === b) { - return a - } - - if (depth > 500) return b - - const array = isPlainArray(a) && isPlainArray(b) - - if (!array && !(isPlainObject(a) && isPlainObject(b))) return b - - const aItems = array ? a : Object.keys(a) - const aSize = aItems.length - const bItems = array ? b : Object.keys(b) - const bSize = bItems.length - const copy: any = array ? new Array(bSize) : {} - - let equalItems = 0 - - for (let i = 0; i < bSize; i++) { - const key: any = array ? i : bItems[i] - const aItem = a[key] - const bItem = b[key] - - if (aItem === bItem) { - copy[key] = aItem - if (array ? i < aSize : hasOwn.call(a, key)) equalItems++ - continue - } - - if ( - aItem === null || - bItem === null || - typeof aItem !== 'object' || - typeof bItem !== 'object' - ) { - copy[key] = bItem - continue - } - - const v = replaceEqualDeep(aItem, bItem, depth + 1) - copy[key] = v - if (v === aItem) equalItems++ - } - - return aSize === bSize && equalItems === aSize ? a : copy -} -``` -The function returns the original reference `a` when `b` is deeply equal to it, so React re-renders only when data genuinely changes. Three early exits handle the trivial cases before the loop; a single `equalItems` counter avoids a second pass. The depth cap at 500 prevents runaway recursion on pathological inputs. - -### `notifyManager.ts` — closure-as-module: all state in let-vars, all behaviour in named consts, returned as `as const` -[source](https://github.com/TanStack/query/blob/0bed37a91efa1b6e84b192ca3629d6e0c6cfcb73/packages/query-core/src/notifyManager.ts) -```ts -export function createNotifyManager() { - let queue: Array = [] - let transactions = 0 - let notifyFn: NotifyFunction = (callback) => { - callback() - } - let batchNotifyFn: BatchNotifyFunction = (callback: () => void) => { - callback() - } - let scheduleFn = defaultScheduler - - const schedule = (callback: NotifyCallback): void => { - if (transactions) { - queue.push(callback) - } else { - scheduleFn(() => { - notifyFn(callback) - }) - } - } - const flush = (): void => { - const originalQueue = queue - queue = [] - if (originalQueue.length) { - scheduleFn(() => { - batchNotifyFn(() => { - originalQueue.forEach((callback) => { - notifyFn(callback) - }) - }) - }) - } - } - - return { - batch: (callback: () => T): T => { - let result - transactions++ - try { - result = callback() - } finally { - transactions-- - if (!transactions) { - flush() - } - } - return result - }, - /** - * All calls to the wrapped function will be batched. - */ - batchCalls: >( - callback: BatchCallsCallback, - ): BatchCallsCallback => { - return (...args) => { - schedule(() => { - callback(...args) - }) - } - }, - schedule, - /** - * Use this method to set a custom notify function. - * This can be used to for example wrap notifications with `React.act` while running tests. - */ - setNotifyFunction: (fn: NotifyFunction) => { - notifyFn = fn - }, - /** - * Use this method to set a custom function to batch notifications together into a single tick. - * By default React Query will use the batch function provided by ReactDOM or React Native. - */ -``` -The singleton is created by calling the factory once at module load. All mutable state (`queue`, `transactions`, `notifyFn`) lives as captured `let` variables — no class, no `this`. The `setNotifyFunction`/`setBatchNotifyFunction` setters exist precisely to let test harnesses swap in `React.act`-wrapped wrappers without touching the core logic. - -### `queryCache.test.tsx` — test shape: fake timers, one assertion per `it`, event-sequence verified as ordered array -[source](https://github.com/TanStack/query/blob/0bed37a91efa1b6e84b192ca3629d6e0c6cfcb73/packages/query-core/src/__tests__/queryCache.test.tsx) -```ts - it('should notify query cache when a query becomes stale', async () => { - const key = queryKey() - const events: Array = [] - const queries: Array = [] - const unsubscribe = queryCache.subscribe((event) => { - events.push(event.type) - queries.push(event.query) - }) - - const observer = new QueryObserver(queryClient, { - queryKey: key, - queryFn: () => 'data', - staleTime: 10, - }) - - const unsubScribeObserver = observer.subscribe(vi.fn()) - - await vi.advanceTimersByTimeAsync(11) - expect(events.length).toBe(8) - - expect(events).toEqual([ - 'added', // 1. Query added -> loading - 'observerResultsUpdated', // 2. Observer result updated -> loading - 'observerAdded', // 3. Observer added - 'observerResultsUpdated', // 4. Observer result updated -> fetching - 'updated', // 5. Query updated -> fetching - 'observerResultsUpdated', // 6. Observer result updated -> success - 'updated', // 7. Query updated -> success - 'observerResultsUpdated', // 8. Observer result updated -> stale - ]) - - queries.forEach((query) => { - expect(query).toBeDefined() - }) - - unsubscribe() - unsubScribeObserver() - }) -``` -Tests collect side-effects into a plain array and then assert the entire sequence in one `toEqual`. The ordered list with inline comments makes the notification contract self-documenting: the expected progression from `'added'` through `'updated'` to `'observerResultsUpdated'` at stale time is pinned as a regression guard, not just a final-state check. diff --git a/packs/zod.md b/packs/zod.md deleted file mode 100644 index 0fa3b03..0000000 --- a/packs/zod.md +++ /dev/null @@ -1,306 +0,0 @@ -## Code like Colin McDonnell (@colinhacks) · parse, don't validate - -Colin builds schemas as immutable value objects: every schema method returns a new instance, every parse call either returns a typed value or pushes structured issues onto a mutable payload — it never throws mid-flight. His type system is structural rather than nominal: `_zod.output` and `_zod.input` are phantom slots on every object so callers get narrowed types without runtime overhead. He avoids class hierarchies in favor of a single `$constructor` factory that installs traits on instances and supports `instanceof` through a `traits` Set, keeping all three flavors of Zod (classic, mini, core) running on the same runtime shape. Errors are discriminated unions keyed on a string `code` so every branch in a `switch` narrows exhaustively without a cast. Async and sync are always separate code paths; encountering a `Promise` in a sync context throws immediately rather than silently coercing. - -### `core.ts` — the `$constructor` factory: traits over inheritance -[source](https://github.com/colinhacks/zod/blob/912f0f51b0ced654d0069741e7160834dca742ee/packages/zod/src/v4/core/core.ts) -```ts -export /*@__NO_SIDE_EFFECTS__*/ function $constructor( - name: string, - initializer: (inst: T, def: D) => void, - params?: { Parent?: typeof Class } -): $constructor { - function init(inst: T, def: D) { - if (!inst._zod) { - Object.defineProperty(inst, "_zod", { - value: { - def, - constr: _, - traits: new Set(), - }, - enumerable: false, - }); - } - - if (inst._zod.traits.has(name)) { - return; - } - - inst._zod.traits.add(name); - - initializer(inst, def); - - // support prototype modifications - const proto = _.prototype; - const keys = Object.keys(proto); - for (let i = 0; i < keys.length; i++) { - const k = keys[i]!; - if (!(k in inst)) { - (inst as any)[k] = proto[k].bind(inst); - } - } - } - - // doesn't work if Parent has a constructor with arguments - const Parent = params?.Parent ?? Object; - class Definition extends Parent {} - Object.defineProperty(Definition, "name", { value: name }); - - function _(this: any, def: D) { - const inst = params?.Parent ? new Definition() : this; - init(inst, def); - inst._zod.deferred ??= []; - for (const fn of inst._zod.deferred) { - fn(); - } - return inst; - } - - Object.defineProperty(_, "init", { value: init }); - Object.defineProperty(_, Symbol.hasInstance, { - value: (inst: any) => { - if (params?.Parent && inst instanceof params.Parent) return true; - return inst?._zod?.traits?.has(name); - }, - }); - Object.defineProperty(_, "name", { value: name }); - return _ as any; -``` -Instead of a class hierarchy, every schema type is defined via this single factory: `init` stamps a `traits` Set onto each instance and the `initializer` callback does all the work. `instanceof` is overridden to check `traits.has(name)`, so the three Zod flavors can share runtime identity checks without sharing a prototype chain. - -### `checks.ts` — a check implementation: push an issue, return, done -[source](https://github.com/colinhacks/zod/blob/912f0f51b0ced654d0069741e7160834dca742ee/packages/zod/src/v4/core/checks.ts) -```ts -export const $ZodCheckLessThan: core.$constructor<$ZodCheckLessThan> = /*@__PURE__*/ core.$constructor( - "$ZodCheckLessThan", - (inst, def) => { - $ZodCheck.init(inst, def); - const origin = numericOriginMap[typeof def.value as "number" | "bigint" | "object"]; - - inst._zod.onattach.push((inst) => { - const bag = inst._zod.bag; - const curr = (def.inclusive ? bag.maximum : bag.exclusiveMaximum) ?? Number.POSITIVE_INFINITY; - if (def.value < curr) { - if (def.inclusive) bag.maximum = def.value; - else bag.exclusiveMaximum = def.value; - } - }); - - inst._zod.check = (payload) => { - if (def.inclusive ? payload.value <= def.value : payload.value < def.value) { - return; - } - - payload.issues.push({ - origin, - code: "too_big", - maximum: typeof def.value === "object" ? def.value.getTime() : def.value, - input: payload.value, - inclusive: def.inclusive, - inst, - continue: !def.abort, - }); - }; - } -); -``` -The check function either returns immediately (fast path on success) or pushes a structured issue literal onto `payload.issues` — never throws. `onattach` hooks update the schema's metadata `bag` when the check is wired to a schema, keeping bag-level summary state (e.g. `maximum`) always current without a second pass. - -### `schemas.ts` — the type-check function shape: one branch per outcome -[source](https://github.com/colinhacks/zod/blob/912f0f51b0ced654d0069741e7160834dca742ee/packages/zod/src/v4/core/schemas.ts) -```ts -export const $ZodString: core.$constructor<$ZodString> = /*@__PURE__*/ core.$constructor("$ZodString", (inst, def) => { - $ZodType.init(inst, def); - inst._zod.pattern = [...(inst?._zod.bag?.patterns ?? [])].pop() ?? regexes.string(inst._zod.bag); - inst._zod.parse = (payload, _) => { - if (def.coerce) - try { - payload.value = String(payload.value); - } catch (_) {} - - if (typeof payload.value === "string") return payload; - - payload.issues.push({ - expected: "string", - code: "invalid_type", - - input: payload.value, - inst, - }); - return payload; - }; -}); -``` -Every primitive parser follows this pattern: try a coercion if configured, then a single type guard that returns `payload` on success, else push one issue literal and return. The payload is always returned — never thrown — so the caller controls abort logic. - -### `registries.ts` — a stateful module: `WeakMap` keyed by schema identity, metadata inherited via parent chain -[source](https://github.com/colinhacks/zod/blob/912f0f51b0ced654d0069741e7160834dca742ee/packages/zod/src/v4/core/registries.ts) -```ts -export class $ZodRegistry { - _meta!: Meta; - _schema!: Schema; - _map: WeakMap> = new WeakMap(); - _idmap: Map = new Map(); - - add( - schema: S, - ..._meta: undefined extends Meta ? [$replace?] : [$replace] - ): this { - const meta: any = _meta[0]; - this._map.set(schema, meta!); - if (meta && typeof meta === "object" && "id" in meta) { - this._idmap.set(meta.id!, schema); - } - return this as any; - } - - clear(): this { - this._map = new WeakMap(); - this._idmap = new Map(); - return this; - } - - remove(schema: Schema): this { - const meta: any = this._map.get(schema); - if (meta && typeof meta === "object" && "id" in meta) { - this._idmap.delete(meta.id!); - } - this._map.delete(schema); - return this; - } - - get(schema: S): $replace | undefined { - // return this._map.get(schema) as any; - - // inherit metadata - const p = schema._zod.parent as Schema; - if (p) { - const pm: any = { ...(this.get(p) ?? {}) }; - delete pm.id; // do not inherit id - const f = { ...pm, ...this._map.get(schema) } as any; - return Object.keys(f).length ? f : undefined; - } - return this._map.get(schema) as any; - } - - has(schema: Schema): boolean { - return this._map.has(schema); - } -} -``` -`WeakMap` keyed by schema object lets the registry hold metadata without preventing GC. The `get` method silently merges parent metadata so cloned schemas inherit description and title without re-registering — `id` is explicitly stripped so clones never share the same JSON Schema `$defs` key. - -### `util.ts` — a characteristic utility: lazy property with cycle detection via a sentinel symbol -[source](https://github.com/colinhacks/zod/blob/912f0f51b0ced654d0069741e7160834dca742ee/packages/zod/src/v4/core/util.ts) -```ts -const EVALUATING = /* @__PURE__*/ Symbol("evaluating"); - -export function defineLazy(object: T, key: K, getter: () => T[K]): void { - let value: T[K] | typeof EVALUATING | undefined = undefined; - Object.defineProperty(object, key, { - get() { - if (value === EVALUATING) { - // Circular reference detected, return undefined to break the cycle - return undefined as T[K]; - } - if (value === undefined) { - value = EVALUATING; - value = getter(); - } - return value; - }, - set(v) { - Object.defineProperty(object, key, { - value: v, - // configurable: true, - }); - // object[key] = v; - }, - configurable: true, - }); -} -``` -A private `Symbol` acts as a sentinel to break recursive access during initialization instead of blowing the stack. The `set` trap replaces the getter descriptor with a plain data descriptor on first write, eliminating the getter overhead for future accesses. - -### `regexes.ts` — naming and module layout: one export per format, functions where parameterization is needed -[source](https://github.com/colinhacks/zod/blob/912f0f51b0ced654d0069741e7160834dca742ee/packages/zod/src/v4/core/regexes.ts) -```ts -export const cuid: RegExp = /^[cC][0-9a-z]{6,}$/; -export const cuid2: RegExp = /^[0-9a-z]+$/; -export const ulid: RegExp = /^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/; -export const xid: RegExp = /^[0-9a-vA-V]{20}$/; -export const ksuid: RegExp = /^[A-Za-z0-9]{27}$/; -export const nanoid: RegExp = /^[a-zA-Z0-9_-]{21}$/; - -/** ISO 8601-1 duration regex. Does not support the 8601-2 extensions like negative durations or fractional/negative components. */ -export const duration: RegExp = - /^P(?:(\d+W)|(?!.*W)(?=\d|T\d)(\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+([.,]\d+)?S)?)?)$/; - -/** Implements ISO 8601-2 extensions like explicit +- prefixes, mixing weeks with other units, and fractional/negative components. */ -export const extendedDuration: RegExp = - /^[-+]?P(?!$)(?:(?:[-+]?\d+Y)|(?:[-+]?\d+[.,]\d+Y$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:(?:[-+]?\d+W)|(?:[-+]?\d+[.,]\d+W$))?(?:(?:[-+]?\d+D)|(?:[-+]?\d+[.,]\d+D$))?(?:T(?=[\d+-])(?:(?:[-+]?\d+H)|(?:[-+]?\d+[.,]\d+H$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:[-+]?\d+(?:[.,]\d+)?S)?)??$/; - -/** A regex for any UUID-like identifier: 8-4-4-4-12 hex pattern */ -export const guid: RegExp = /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/; - -/** Returns a regex for validating an RFC 9562/4122 UUID. - * - * @param version Optionally specify a version 1-8. If no version is specified, all versions are supported. */ -export const uuid = (version?: number | undefined): RegExp => { - if (!version) - return /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/; - return new RegExp( - `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${version}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$` - ); -}; -export const uuid4: RegExp = /*@__PURE__*/ uuid(4); -export const uuid6: RegExp = /*@__PURE__*/ uuid(6); -export const uuid7: RegExp = /*@__PURE__*/ uuid(7); -``` -Format constants are named after their format, typed as `RegExp`, and documented with JSDoc linking to the spec. Where a regex varies by parameter (UUID version, datetime precision) he uses a plain function that returns a `RegExp`; the common versions are pre-computed as named constants (`uuid4`, `uuid6`, `uuid7`) to avoid redundant allocation. - -### `classic/tests/error.test.ts` — test shape: `safeParse` returns a tagged result, errors pinned with inline snapshots -[source](https://github.com/colinhacks/zod/blob/912f0f51b0ced654d0069741e7160834dca742ee/packages/zod/src/v4/classic/tests/error.test.ts) -```ts -test("array minimum", () => { - let result = z.array(z.string()).min(3, "tooshort").safeParse(["asdf", "qwer"]); - expect(result.success).toBe(false); - expect(result.error!.issues[0].code).toEqual("too_small"); - expect(result.error!.issues[0].message).toEqual("tooshort"); - - result = z.array(z.string()).min(3).safeParse(["asdf", "qwer"]); - expect(result.success).toBe(false); - expect(result.error!.issues[0].code).toEqual("too_small"); - expect(result.error).toMatchInlineSnapshot(` - [ZodError: [ - { - "origin": "array", - "code": "too_small", - "minimum": 3, - "inclusive": true, - "path": [], - "message": "Too small: expected array to have >=3 items" - } - ]] - `); -}); - -test("literal bigint default error message", () => { - const result = z.literal(BigInt(12)).safeParse(BigInt(13)); - expect(result.success).toBe(false); - expect(result.error!.issues.length).toEqual(1); - expect(result.error).toMatchInlineSnapshot(` - [ZodError: [ - { - "code": "invalid_value", - "values": [ - "12" - ], - "path": [], - "message": "Invalid input: expected 12n" - } - ]] - `); -}); -``` -Tests call `safeParse` (never `parse`) so there's no try/catch noise; the `result.success` branch narrows the type. `toMatchInlineSnapshot` pins the full serialized `ZodError` — `code`, `path`, and `message` — so regressions in any field of the discriminated union surface immediately without separate assertions for each field. diff --git a/site/src/pages/index.astro b/site/src/pages/index.astro index 1d18e36..53b4dfe 100644 --- a/site/src/pages/index.astro +++ b/site/src/pages/index.astro @@ -34,18 +34,6 @@ const gallery = [ }, ]; -const packs = [ - ['dtolnay', 'dtolnay.md'], - ['DHH', 'dhh.md'], - ['antirez', 'antirez.md'], - ['Sindre Sorhus', 'sindre-sorhus.md'], - ['Rich Harris', 'rich-harris.md'], - ['colinhacks', 'zod.md'], - ['Mitchell Hashimoto', 'mitchell-hashimoto.md'], - ['Tanner Linsley', 'tanner-linsley.md'], - ['Simon Willison', 'simon-willison.md'], - ['Jarred Sumner', 'jarred-sumner.md'], -]; --- @@ -80,7 +68,6 @@ const packs = [ 🧙 stupify @@ -96,10 +83,7 @@ const packs = [ slop.

- stupify reviews every PR against a corpus of code - you - actually - respect, names + stupify reviews every PR against a corpus of code you actually respect, names the slop, and tells you how to fix it.

@@ -169,28 +153,6 @@ const packs = [
-
-
-
-

No corpus yet? Borrow one

-

- Pick a programmer whose code you’d point a new hire at, or compose several. Each pack is - concrete principles plus commit-pinned exemplar files. Or bring your own best files. -

-
-
- { - packs.map(([name, file]) => ( - - {name} - - )) - } - browse all → -
-
-
-

Stop shipping slop

diff --git a/site/src/styles/global.css b/site/src/styles/global.css index 370174a..472db4e 100644 --- a/site/src/styles/global.css +++ b/site/src/styles/global.css @@ -354,29 +354,6 @@ figure { line-height: 1.6; } -/* ---- packs ---- */ -.packs { - display: flex; - flex-wrap: wrap; - gap: 0.6rem; -} -.pack { - font-family: var(--mono); - font-size: 0.86rem; - color: var(--ink-dim); - background: var(--card); - border: 1px solid var(--line); - border-radius: 7px; - padding: 0.42rem 0.8rem; - transition: - color 0.15s, - border-color 0.15s; -} -.pack:hover { - color: var(--ink); - border-color: var(--accent); -} - /* ---- closing ---- */ .closing h2 { font-family: var(--serif); diff --git a/src/cli.ts b/src/cli.ts index fe2704c..87e8447 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -27,14 +27,14 @@ import { vmNameFor as packageVmNameFor, writeCodexGatewayConfig, } from '@bevyl-ai/agent-tools' -import { cancel, confirm, intro, isCancel, log, multiselect, note, outro, spinner, text } from '@clack/prompts' +import { cancel, confirm, intro, isCancel, log, note, outro, spinner, text } from '@clack/prompts' import pc from 'picocolors' import { z } from 'zod' import { SweepStatus } from './sweep/status' const PKG_DIR = dirname(fileURLToPath(import.meta.url)) -const PKG_ROOT = join(PKG_DIR, '..') // the published package root: holds .review/ and packs/ +const PKG_ROOT = join(PKG_DIR, '..') // the published package root: holds .review/ const VERSION = z .object({ version: z.string() }) .parse(JSON.parse(readFileSync(join(PKG_ROOT, 'package.json'), 'utf8'))).version @@ -43,25 +43,6 @@ const STATE = join(HOME, 'state') const REQUIRED = ['bun', 'gh', 'codex', 'git'] as const const vmNameFor = (repo: string): string => packageVmNameFor('stupify', repo) -// Taste packs: "code like X". Picking one (or several) seeds the corpus, so you don't start from a blank file. -interface Pack { - id: string - label: string -} -const PACKS: Pack[] = [ - { id: 'sindre-sorhus', label: 'Sindre Sorhus · one file, one job' }, - { id: 'zod', label: 'Colin McDonnell / Zod · parse, don’t validate' }, - { id: 'rich-harris', label: 'Rich Harris / Svelte · compiler-grade precision' }, - { id: 'tanner-linsley', label: 'Tanner Linsley / TanStack · types forbid bad states' }, - { id: 'simon-willison', label: 'Simon Willison · one concept per file' }, - { id: 'dtolnay', label: 'David Tolnay · the API that disappears (Rust)' }, - { id: 'antirez', label: 'antirez / Redis · comments that earn their keep (C)' }, - { id: 'dhh', label: 'DHH / Rails · controllers that tell the story (Ruby)' }, - { id: 'mitchell-hashimoto', label: 'Mitchell Hashimoto / Ghostty · documented tradeoffs' }, - { id: 'devshorts', label: 'devshorts · DI + branded types' }, - { id: 'jarred-sumner', label: 'Jarred Sumner / Bun · perf as correctness' }, -] - function bail(value: T | symbol): asserts value is T { if (isCancel(value)) { cancel('aborted.') @@ -121,93 +102,6 @@ function progress(start: string): { stop: (msg: string) => void } { return { stop: (msg: string) => s.stop(msg) } } -// The short human label for a set of picked packs, e.g. "Sindre Sorhus + devshorts" — for plan/success notes. -const tasteLabel = (packs: string[]): string => - PACKS.filter((p) => packs.includes(p.id)) - .map((p) => p.label.split(' · ')[0]) - .join(' + ') - -// Returns the chosen pack ids. `--pack a,b` (or 'own'/'' = your own codebase) skips the prompt; with --yes and -// no flag it defaults to sindre-sorhus (the broadly-applicable TS/JS taste) so a fresh repo reviews immediately. -async function pickPacks(opts: { yes: boolean; packArg?: string | undefined }): Promise { - if (opts.packArg !== undefined) { - const requested = opts.packArg - .split(',') - .map((s) => s.trim().toLowerCase()) - .filter(Boolean) - const known = (id: string) => PACKS.some((p) => p.id === id) - const unknown = requested.filter((id) => id !== 'own' && !known(id)) - if (unknown.length > 0) { - log.warn(`unknown pack(s): ${pc.bold(unknown.join(', '))}, valid: ${PACKS.map((p) => p.id).join(', ')}`) - } - return requested.filter((id) => id !== 'own' && known(id)) - } - if (opts.yes) { - return ['sindre-sorhus'] - } - if (!process.stdin.isTTY) { - return [] - } // non-interactive (CI, scripts, the install hook): never block on a picker - const choice = await multiselect({ - message: 'Whose code should yours look like? (pick any, or your own)', - options: [ - ...PACKS.map((p) => ({ value: p.id, label: p.label })), - { value: 'own', label: '🧠 my own codebase', hint: 'point CORPUS.md at your files yourself' }, - ], - required: false, - }) - bail(choice) - return choice.filter((v) => v !== 'own') -} - -// Build ~/.stupify/.review from the bundled rubric/prompt + the chosen packs' corpus. The engine uses this when -// the target repo has no .review/ of its own — so taste packs work with zero files in your repo. -function assembleReview(packs: string[]): void { - const out = join(HOME, '.review') - mkdirSync(out, { recursive: true }) - copyFileSync(join(PKG_ROOT, '.review', 'RUBRIC.md'), join(out, 'RUBRIC.md')) - copyFileSync(join(PKG_ROOT, '.review', 'REVIEW-PROMPT.md'), join(out, 'REVIEW-PROMPT.md')) - if (packs.length === 0) { - return - } // no packs → no global corpus; reviewer/prime honestly no-op until you add taste - // (the bring-your-own template is scaffolded into a repo by `stupify init`, never written as a usable global corpus) - const header = `# Good-code reference — taste packs\n\nJudge every diff against the standards below. When you flag slop, name the principle (or the linked file) the change should have followed. Each entry inlines real code from the named programmer, with a commit-pinned source link.\n\n---\n\n` - const body = packs.map((id) => readFileSync(join(PKG_ROOT, 'packs', `${id}.md`), 'utf8').trim()).join('\n\n---\n\n') - writeFileSync(join(out, 'CORPUS.md'), `${header}${body}\n`) -} - -// `stupify taste [--pack a,b]` — assemble your GLOBAL taste at ~/.stupify/.review from packs, and nothing else. -// This is the shared core both the reviewer and `stupify prime` read when a repo has no .review/ of its own — -// so you can set taste once without installing the cron reviewer. -async function taste(argv: { pack?: string | undefined; yes: boolean }): Promise { - console.clear() - intro(pc.bgMagenta(pc.black(' stupify ')) + pc.dim(' · pick the code yours should look like')) - const packs = await pickPacks({ yes: argv.yes, packArg: argv.pack }) - if (packs.length === 0) { - note( - [ - `no packs picked. taste packs seed a global corpus at ${pc.cyan(join(HOME, '.review'))}.`, - `want YOUR OWN code as the standard? ${pc.cyan('stupify init ')} scaffolds a ${pc.cyan('.review/')} in your repo ${pc.dim('(it always wins over a pack)')}.`, - ].join('\n'), - 'nothing to assemble', - ) - outro(pc.dim('pass --pack for a pack, or `stupify init` for your own taste.')) - return - } - assembleReview(packs) - const tasteLine = tasteLabel(packs) - note( - [ - `assembled ${pc.cyan(join(HOME, '.review'))} against ${pc.bold(tasteLine)}.`, - `your global taste, read by the reviewer AND ${pc.cyan('stupify prime')} in any repo without its own .review/.`, - ``, - `${pc.bold('next:')} ${pc.cyan('stupify prime --install')} ${pc.dim('· prime Claude Code with it every session')}`, - ].join('\n'), - 'taste ready', - ) - outro(pc.green('your taste is set 🎯')) -} - // Fence language tag from a file extension — best-effort, blank when unknown (still renders fine). const LANG: Record = { ts: 'ts', @@ -247,8 +141,8 @@ function repoRoot(): { root: string; inGit: boolean } { const CORPUS_CAP = 150 // lines: a single exemplar past this gets truncated (a corpus is shapes, not whole files) -// `stupify init [files…]` — scaffold a BYO `.review/` in THIS repo from your own best files (no famous-coder -// pack). Writes the rubric + review spec (defaults, kept if already present) and builds CORPUS.md by inlining +// `stupify init [files…]` — scaffold a `.review/` in THIS repo from your own best files. +// Writes the rubric + review spec (defaults, kept if already present) and builds CORPUS.md by inlining // each file you name with a one-line "why" for you to fill — the only hand-work, and the irreducible taste part. const WHY_PLACEHOLDER = '⟨why is this good? one line, e.g. "fail-fast at the boundary"⟩' @@ -358,7 +252,6 @@ async function setup(argv: { host?: string | undefined codexHost?: string | undefined yes: boolean - pack?: string | undefined }): Promise { console.clear() intro(pc.bgMagenta(pc.black(' stupify ')) + pc.dim(' · sounds dumb, reviews sharp')) @@ -428,15 +321,10 @@ async function setup(argv: { die(`'${argv.codexHost}' is not a valid Codex gateway host, hostname characters only`) } - // 3.5 taste — pick a pack (or your own code) - const packs = await pickPacks({ yes: argv.yes, packArg: argv.pack }) - const tasteLine = packs.length > 0 ? tasteLabel(packs) : 'your own codebase' - // 4. plan + confirm note( [ `${pc.dim('repo ')} ${pc.bold(repo)}`, - `${pc.dim('taste ')} ${pc.bold(tasteLine)}`, host ? `${pc.dim('auth ')} exe.dev integration ${pc.bold(host)} ${pc.dim('· exe-llm gateway, no keys')}` : `${pc.dim('auth ')} your own gh + codex ${pc.dim('(run `gh auth login` first)')}`, @@ -458,7 +346,6 @@ async function setup(argv: { const s2 = progress('installing') mkdirSync(STATE, { recursive: true }) await installSweepEngine() - assembleReview(packs) const cfg = [`REPO_SLUG=${repo}`, host ? `GH_HOST=${host}` : '', '# tune anything else here, see the README'] .filter(Boolean) .join('\n') @@ -489,32 +376,17 @@ async function setup(argv: { const preview = `${pc.dim('preview anytime:')} ${pc.cyan(`DRY_RUN=1 bun ${join(HOME, 'review-sweep.ts')}`)}` const statusLine = `${pc.dim('status anytime: ')} ${pc.cyan('stupify status')}` const githubLine = `${pc.dim('github status: ')} ${pc.cyan('stupify/review')} ${pc.dim('on each PR head commit')}` - if (packs.length > 0) { - note( - [ - `reviewing ${pc.bold(repo)} against ${pc.bold(tasteLine)}.`, - `open a PR (or push to one) → stupify reviews it in ~60s. ${pc.dim('no labels, no setup.')}`, - ``, - `want your OWN taste instead? add a ${pc.cyan('.review/')} to ${pc.bold(repo)}, it overrides the pack.`, - githubLine, - preview, - statusLine, - ].join('\n'), - "you're set", - ) - } else { - note( - [ - `${pc.bold('1.')} add a ${pc.cyan('.review/')} to ${pc.bold(repo)} and point ${pc.cyan('CORPUS.md')} at YOUR best files`, - `${pc.bold('2.')} open a PR → stupify reviews it in ~60s ${pc.dim('(no labels needed)')}`, - ``, - githubLine, - preview, - statusLine, - ].join('\n'), - 'two steps to your first review', - ) - } + note( + [ + `${pc.bold('1.')} ${pc.cyan('stupify init ')} in ${pc.bold(repo)} scaffolds a ${pc.cyan('.review/')}; point ${pc.cyan('CORPUS.md')} at YOUR best files`, + `${pc.bold('2.')} open a PR → stupify reviews it in ~60s ${pc.dim('(no labels needed)')}`, + ``, + githubLine, + preview, + statusLine, + ].join('\n'), + 'two steps to your first review', + ) outro(pc.green('stupify is watching ') + pc.bold(repo) + pc.green(' 👀')) } @@ -660,31 +532,20 @@ function removeHook(file: string): { removed: boolean } { const hasTaste = (d: string): boolean => existsSync(join(d, 'RUBRIC.md')) && existsSync(join(d, 'CORPUS.md')) -async function installPrimeHook(argv: { pack?: string | undefined; agent?: string | undefined }): Promise { +function installPrimeHook(argv: { agent?: string | undefined }): void { console.clear() const targets = selectTargets(argv.agent) intro( pc.bgMagenta(pc.black(' stupify ')) + pc.dim(` · prime ${targets.map((t) => t.label).join(' + ')} with your taste`), ) - // 0. ensure GLOBAL taste exists for the hook to inject. The hook runs in EVERY repo; a repo's own .review/ - // wins, but ~/.stupify/.review is the fallback, so without it the hook would no-op everywhere. Assemble it - // here (explicit --pack always (re)assembles; otherwise pick only when none exists) so install just works. - const haveHomeTaste = hasTaste(join(HOME, '.review')) - const haveRepoTaste = hasTaste(join(repoRoot().root, '.review')) // a BYO .review/ in the repo you're standing in - let primed = haveHomeTaste || haveRepoTaste - if (argv.pack !== undefined || !primed) { - const packs = await pickPacks({ yes: false, packArg: argv.pack }) - if (packs.length > 0) { - assembleReview(packs) - primed = true - const tasteLine = tasteLabel(packs) - log.success(`global taste assembled → ${pc.cyan(join(HOME, '.review'))} ${pc.dim(`(${tasteLine})`)}`) - } else if (!primed) { - log.warn( - `no taste yet. the hook will no-op until this repo has a ${pc.cyan('.review/')} (${pc.cyan('stupify init')}) or you run ${pc.cyan('stupify taste')}`, - ) - } + // 0. the hook runs in EVERY repo: the repo's own .review/ wins, ~/.stupify/.review is the fallback. Without + // either it no-ops, so say so up front. + const primed = hasTaste(join(HOME, '.review')) || hasTaste(join(repoRoot().root, '.review')) + if (!primed) { + log.warn( + `no taste yet. the hook will no-op until this repo has a ${pc.cyan('.review/')} (${pc.cyan('stupify init')})`, + ) } // 1. drop the dep-free emitter where the hook can run it fast, no global install needed @@ -705,7 +566,7 @@ async function installPrimeHook(argv: { pack?: string | undefined; agent?: strin ``, primed ? `every new session now opens primed with your taste ${pc.dim('(~30ms, pure file read)')}.` - : `wired but ${pc.bold('dormant')}. it activates in any repo with a ${pc.cyan('.review/')}, or run ${pc.cyan('stupify taste --pack ')} to set a global one.`, + : `wired but ${pc.bold('dormant')}. it activates in any repo with a ${pc.cyan('.review/')} (${pc.cyan('stupify init')}).`, ``, `${pc.dim('undo:')} ${pc.cyan('stupify prime --uninstall')}`, ].join('\n'), @@ -846,7 +707,7 @@ function cmdReview(ref: string | undefined, post: boolean): void { // --- provision: spin up an exe.dev VM that runs stupify, from your laptop --- -async function provision(argv: { repo?: string | undefined; yes: boolean; pack?: string | undefined }): Promise { +async function provision(argv: { repo?: string | undefined; yes: boolean }): Promise { console.clear() intro(pc.bgMagenta(pc.black(' stupify ')) + pc.dim(' · provision a reviewer on exe.dev')) @@ -896,9 +757,6 @@ async function provision(argv: { repo?: string | undefined; yes: boolean; pack?: die(`'${repo}' is not a valid owner/repo, expected owner/repo (e.g. acme/widgets)`) } - // 2.5 taste — pick a pack (or your own code); the VM installs it on first boot - const packs = await pickPacks({ yes: argv.yes, packArg: argv.pack }) - // 3. GitHub integration — reuse an existing one, else create it (needs your GitHub linked once, on the web) const s2 = progress('finding your GitHub integration') let integration = githubIntegrationFor(repo) @@ -933,13 +791,10 @@ async function provision(argv: { repo?: string | undefined; yes: boolean; pack?: if (llm && !validHost(llm)) { die(`exe.dev returned an unexpected exe-llm integration name (${llm}). refusing to use it`) } - const tasteLine = packs.length > 0 ? tasteLabel(packs) : 'your own codebase' - // 4. plan + confirm note( [ `${pc.dim('repo ')} ${pc.bold(repo)}`, - `${pc.dim('taste')} ${pc.bold(tasteLine)}`, `${pc.dim('vm ')} a small always-on exe.dev VM on your account`, `${pc.dim('auth ')} integration ${pc.bold(integration)} ${pc.dim('· no keys, no tokens')}`, ].join('\n'), @@ -957,7 +812,7 @@ async function provision(argv: { repo?: string | undefined; yes: boolean; pack?: // 5. create the VM with a first-boot setup-script that installs stupify const s3 = progress('provisioning VM + installing stupify') const vm = vmNameFor(repo) - const setupCommand = `exec bunx @stupify/cli@${VERSION} setup ${repo} --host ${host} --pack ${packs.join(',') || 'own'} --yes` + const setupCommand = `exec bunx @stupify/cli@${VERSION} setup ${repo} --host ${host} --yes` const script = exeSetupScript(setupCommand, llm ? `${llm}.int.exe.xyz` : undefined) const created = exe( ['new', '--name', vm, '--integration', integration, '--json', '--setup-script', '/dev/stdin'], @@ -986,19 +841,10 @@ async function provision(argv: { repo?: string | undefined; yes: boolean; pack?: } // 6. success - const firstReview = - packs.length > 0 - ? [ - `reviewing ${pc.bold(repo)} against ${pc.bold(tasteLine)}.`, - `open a PR (or push to one) → stupify reviews it in ~60s. ${pc.dim('no labels, no setup.')}`, - ``, - `want your OWN taste? add a ${pc.cyan('.review/')} to ${pc.bold(repo)}, it overrides the pack.`, - ] - : [ - `${pc.yellow('⚠ dormant')}. you chose your own taste, so the reviewer no-ops every sweep until ${pc.bold(repo)} has a ${pc.cyan('.review/')}.`, - `${pc.bold('1.')} add a ${pc.cyan('.review/')} to ${pc.bold(repo)}, copy this repo's, point CORPUS.md at YOUR best files`, - `${pc.bold('2.')} push it → stupify reviews every PR in ~60s ${pc.dim('(no labels needed)')}`, - ] + const firstReview = [ + `reviewing ${pc.bold(repo)} against its own ${pc.cyan('.review/')}. ${pc.dim('no labels, no setup.')}`, + `no ${pc.cyan('.review/')} there yet? ${pc.cyan('stupify init ')} scaffolds one; the reviewer no-ops every sweep until it lands.`, + ] note( [ `${pc.bold(vm)} is booting and installing stupify ${pc.dim('(~15s)')}.`, @@ -1026,7 +872,6 @@ ${pc.dim('Usage')} ${pc.dim('(run from your laptop)')} stupify status show the latest sweep as a workflow stupify upgrade [repo] move a running reviewer to the latest engine, in place ${pc.dim('(a VM if repo given, else this box)')} stupify review [--post] review ONE pull request on demand (a URL or owner/repo#123); prints it, --post comments it - stupify taste [--pack a,b] borrow a taste pack (assembles ~/.stupify/.review); packs below stupify init [files…] encode YOUR OWN taste: scaffold .review/ from your best files in this repo stupify prime --install prime Claude Code + Codex with your taste every session (SessionStart hook) stupify prime --uninstall remove those hooks @@ -1036,7 +881,6 @@ ${pc.dim('Flags')} --host GitHub integration host (for 'setup') --codex-host exe-llm gateway host (for 'setup'; default llm.int.exe.xyz) --agent ('prime') which agents to wire: ${PRIME_TARGETS.map((t) => t.id).join(', ')} (default: detected) - --pack taste packs: ${PACKS.map((p) => p.id).join(', ')} --force ('init') rebuild CORPUS.md even if it exists (your filled-in "why" lines are kept) --yes, -y accept detected defaults, no prompts (for CI / scripts) @@ -1095,27 +939,20 @@ const valueFlag = (name: string) => { } const host = valueFlag('--host') const codexHost = valueFlag('--codex-host') -const pack = valueFlag('--pack') const agent = valueFlag('--agent') const positional = args.filter( (a, i) => - !a.startsWith('-') && - args[i - 1] !== '--host' && - args[i - 1] !== '--codex-host' && - args[i - 1] !== '--pack' && - args[i - 1] !== '--agent', + !a.startsWith('-') && args[i - 1] !== '--host' && args[i - 1] !== '--codex-host' && args[i - 1] !== '--agent', ) const [cmd] = positional if (args.includes('-h') || args.includes('--help') || cmd === 'help') { help() -} else if (cmd === 'taste') { - await taste({ pack, yes }) } else if (cmd === 'init') { await init({ files: positional.slice(1), force: args.includes('--force') }) } else if (cmd === 'prime') { if (args.includes('--install')) { - await installPrimeHook({ pack, agent }) + installPrimeHook({ agent }) } else if (args.includes('--uninstall')) { uninstallPrimeHook() } else { @@ -1130,10 +967,10 @@ if (args.includes('-h') || args.includes('--help') || cmd === 'help') { } else if (cmd === 'review') { cmdReview(positional[1], args.includes('--post')) } else if (cmd === 'setup') { - await setup({ repo: positional[1], host, codexHost, yes, pack }) + await setup({ repo: positional[1], host, codexHost, yes }) } else if (cmd === 'upgrade') { await upgrade(positional[1]) } else { // default (and explicit `provision`): provision an exe.dev VM - await provision({ repo: cmd === 'provision' ? positional[1] : cmd, yes, pack }) + await provision({ repo: cmd === 'provision' ? positional[1] : cmd, yes }) } diff --git a/src/init.test.ts b/src/init.test.ts deleted file mode 100644 index e15527b..0000000 --- a/src/init.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -// Guards `stupify init` (bring-your-own scaffold). The invariants the user-sim found blockers in: CORPUS paths -// are repo-root-relative (portable + correct from a subdir), real code is inlined, and a --force rebuild -// PRESERVES the user's hand-written "why" lines. Driven through the real CLI subprocess in throwaway git repos. -import { expect, test } from 'bun:test' -import { spawnSync, type SpawnSyncReturns } from 'node:child_process' -import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs' -import { tmpdir } from 'node:os' -import { join } from 'node:path' - -const CLI = join(import.meta.dir, 'cli.ts') -type CliResult = SpawnSyncReturns - -function repo() { - const root = mkdtempSync(join(tmpdir(), 'stupify-init-')) - spawnSync('git', ['init', '-q'], { cwd: root }) - mkdirSync(join(root, 'src'), { recursive: true }) - writeFileSync(join(root, 'src/a.ts'), 'export const QueueName = (s: string) => s\n') - writeFileSync(join(root, 'src/b.ts'), 'export const b = 2\n') - return root -} -function initIn(cwd: string, sub: string[]): CliResult { - return spawnSync('bun', [CLI, 'init', ...sub], { cwd, encoding: 'utf8' }) -} -function expectSuccess(result: CliResult): void { - expect(result.status, result.stderr || result.stdout).toBe(0) -} -const corpus = (root: string) => readFileSync(join(root, '.review', 'CORPUS.md'), 'utf8') -const clean = (root: string) => rmSync(root, { recursive: true, force: true }) - -test('init writes repo-root-relative paths + inlines real code, even run from a subdir', () => { - const root = repo() - mkdirSync(join(root, 'src/deep'), { recursive: true }) - expectSuccess(initIn(join(root, 'src/deep'), ['../a.ts'])) // from a subdir, with a cwd-relative path - const c = corpus(root) - expect(c).toContain('### `src/a.ts`') // repo-root-relative, NOT ../a.ts - expect(c).not.toContain('../a.ts') - expect(c).toContain('QueueName') // real code inlined - clean(root) -}) - -test('init --force preserves filled-in "why" lines and adds new files', () => { - const root = repo() - expectSuccess(initIn(root, ['src/a.ts'])) - const p = join(root, '.review', 'CORPUS.md') - writeFileSync( - p, - readFileSync(p, 'utf8').replace(/^(?### `src\/a\.ts` — ).*$/m, '$branded value, fails fast'), - ) - expectSuccess(initIn(root, ['src/a.ts', 'src/b.ts', '--force'])) - const c = corpus(root) - expect(c).toContain('### `src/a.ts` — branded value, fails fast') // kept - expect(c).toContain('### `src/b.ts`') // added - clean(root) -}) - -test('init refuses to overwrite an existing CORPUS without --force', () => { - const root = repo() - expectSuccess(initIn(root, ['src/a.ts'])) - const before = corpus(root) - expectSuccess(initIn(root, ['src/b.ts'])) // no --force - expect(corpus(root)).toBe(before) // untouched - clean(root) -}) - -test('init writes all three files (RUBRIC + REVIEW-PROMPT + CORPUS) for a consistent .review/', () => { - const root = repo() - expectSuccess(initIn(root, ['src/a.ts'])) - for (const f of ['RUBRIC.md', 'REVIEW-PROMPT.md', 'CORPUS.md']) { - expect(readFileSync(join(root, '.review', f), 'utf8').length).toBeGreaterThan(0) - } - clean(root) -}) diff --git a/src/prime-install.test.ts b/src/prime-install.test.ts deleted file mode 100644 index 9b83f30..0000000 --- a/src/prime-install.test.ts +++ /dev/null @@ -1,205 +0,0 @@ -// Guards the hook installer's contract — it writes a SessionStart hook into each agent's hooks file (Claude -// Code's settings.json, Codex's hooks.json), so the invariants that matter are: MERGE (never clobber other -// hooks/keys), IDEMPOTENT (no duplicate), SURGICAL uninstall (remove only ours), and REFUSE malformed JSON. -// Driven through the real CLI subprocess against throwaway STUPIFY_HOME + CLAUDE_CONFIG_DIR + CODEX_HOME dirs, -// so the real ~/.claude and ~/.codex are never touched. -import { expect, test } from 'bun:test' -import { spawnSync } from 'node:child_process' -import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs' -import { tmpdir } from 'node:os' -import { join } from 'node:path' - -const CLI = join(import.meta.dir, 'cli.ts') - -function env() { - const home = mkdtempSync(join(tmpdir(), 'stupify-home-')) - const cfg = mkdtempSync(join(tmpdir(), 'stupify-cc-')) - const codex = mkdtempSync(join(tmpdir(), 'stupify-cx-')) - return { home, cfg, codex, settings: join(cfg, 'settings.json'), codexHooks: join(codex, 'hooks.json') } -} -const run = (sub: string[], e: { home: string; cfg: string; codex: string }) => - spawnSync('bun', [CLI, ...sub], { - env: { ...process.env, STUPIFY_HOME: e.home, CLAUDE_CONFIG_DIR: e.cfg, CODEX_HOME: e.codex }, - encoding: 'utf8', - }) -const read = (p: string) => JSON.parse(readFileSync(p, 'utf8')) -const clean = (e: { home: string; cfg: string; codex: string }) => { - for (const d of [e.home, e.cfg, e.codex]) { - rmSync(d, { recursive: true, force: true }) - } -} -const seeded = JSON.stringify({ - theme: 'dark', - hooks: { PostToolUse: [{ matcher: 'Edit', hooks: [{ type: 'command', command: 'echo keep' }] }] }, -}) - -test('--install merges into existing settings without clobbering, and is idempotent', () => { - const e = env() - writeFileSync(e.settings, seeded) - run(['prime', '--install', '--agent', 'claude'], e) - let s = read(e.settings) - expect(s.theme).toBe('dark') // unrelated key survives - expect(s.hooks.PostToolUse[0].hooks[0].command).toBe('echo keep') // unrelated hook survives - expect(s.hooks.SessionStart[0].matcher).toBe('startup') - expect(s.hooks.SessionStart[0].hooks[0].command).toContain('prime.ts') // points at the copied dep-free engine - expect(existsSync(join(e.home, 'prime.ts'))).toBe(true) // engine copied - - run(['prime', '--install', '--agent', 'claude'], e) // again - s = read(e.settings) - expect(s.hooks.SessionStart).toHaveLength(1) // no duplicate - clean(e) -}) - -test('--uninstall removes only our hook + engine, preserving everything else', () => { - const e = env() - writeFileSync(e.settings, seeded) - run(['prime', '--install', '--agent', 'claude'], e) - run(['prime', '--uninstall'], e) - const s = read(e.settings) - expect(s.theme).toBe('dark') - expect(s.hooks.PostToolUse[0].hooks[0].command).toBe('echo keep') - expect(s.hooks.SessionStart).toBeUndefined() // ours gone; empty array collapsed - expect(existsSync(join(e.home, 'prime.ts'))).toBe(false) // engine removed - clean(e) -}) - -test('--install refuses to clobber a malformed settings.json', () => { - const e = env() - writeFileSync(e.settings, 'NOT JSON {') - const r = run(['prime', '--install', '--agent', 'claude'], e) - expect(readFileSync(e.settings, 'utf8')).toContain('NOT JSON') // left untouched - expect(r.status).not.toBe(0) // died non-zero rather than overwrite - clean(e) -}) - -test('--uninstall on a machine with no settings.json is a clean no-op', () => { - const e = env() - const r = run(['prime', '--uninstall'], e) - expect(r.status).toBe(0) - expect(existsSync(e.settings)).toBe(false) - clean(e) -}) - -test('--agent codex wires ~/.codex/hooks.json (startup|resume) and leaves Claude untouched', () => { - const e = env() - run(['prime', '--install', '--agent', 'codex', '--pack', 'zod'], e) - const s = read(e.codexHooks) - expect(s.hooks.SessionStart[0].matcher).toBe('startup|resume') // codex fires on new + resumed sessions - expect(s.hooks.SessionStart[0].hooks[0].command).toContain('prime.ts') // same dep-free emitter - expect(existsSync(e.settings)).toBe(false) // only codex selected → Claude settings not created - clean(e) -}) - -test('--agent claude,codex wires both, and --uninstall sweeps both', () => { - const e = env() - run(['prime', '--install', '--agent', 'claude,codex', '--pack', 'zod'], e) - expect(read(e.settings).hooks.SessionStart[0].matcher).toBe('startup') - expect(read(e.codexHooks).hooks.SessionStart[0].matcher).toBe('startup|resume') - run(['prime', '--uninstall'], e) - expect(read(e.settings).hooks?.SessionStart).toBeUndefined() // ours gone; empty hooks object collapsed - expect(read(e.codexHooks).hooks?.SessionStart).toBeUndefined() - clean(e) -}) - -test('--agent codex merges into an existing hooks.json without clobbering', () => { - const e = env() - writeFileSync(e.codexHooks, seeded) // a pre-existing user hooks.json with an unrelated PostToolUse hook - run(['prime', '--install', '--agent', 'codex', '--pack', 'zod'], e) - const s = read(e.codexHooks) - expect(s.theme).toBe('dark') - expect(s.hooks.PostToolUse[0].hooks[0].command).toBe('echo keep') // their hook survives - expect(s.hooks.SessionStart[0].hooks[0].command).toContain('prime.ts') // ours added alongside - clean(e) -}) - -test('--agent with an unknown name errors out instead of silently doing nothing', () => { - const e = env() - const r = run(['prime', '--install', '--agent', 'emacs'], e) - expect(r.status).not.toBe(0) - expect(r.stdout + r.stderr).toContain('unknown --agent') - expect(existsSync(e.settings)).toBe(false) // nothing wired - clean(e) -}) - -test('taste --pack assembles ~/.stupify/.review and nothing else (no reviewer leaks in)', () => { - const e = env() - run(['taste', '--pack', 'devshorts,zod'], e) - expect(readFileSync(join(e.home, '.review', 'CORPUS.md'), 'utf8')).toContain('devshorts') // packs assembled - expect(existsSync(join(e.home, 'config.env'))).toBe(false) // no reviewer config - expect(existsSync(join(e.home, 'review-sweep.ts'))).toBe(false) // no reviewer engine - clean(e) -}) - -test('--install refreshes a stale command on re-install (not just the engine file)', () => { - const e = env() - run(['prime', '--install', '--agent', 'claude', '--pack', 'zod'], e) - // simulate bun having moved since first install: rewrite the stored command to a stale path - // (keep the engine path so the entry is still recognized as ours) - const s = read(e.settings) - s.hooks.SessionStart[0].hooks[0].command = `/old/stale/bun ${join(e.home, 'prime.ts')}` - writeFileSync(e.settings, JSON.stringify(s)) - run(['prime', '--install', '--agent', 'claude', '--pack', 'zod'], e) - const after = read(e.settings) - expect(after.hooks.SessionStart).toHaveLength(1) // still no duplicate - expect(after.hooks.SessionStart[0].hooks[0].command).not.toContain('/old/stale/bun') // stale path corrected - expect(after.hooks.SessionStart[0].hooks[0].command).toContain('prime.ts') - clean(e) -}) - -test('prime --install --pack assembles taste AND wires the hook in one step', () => { - const e = env() - run(['prime', '--install', '--agent', 'claude', '--pack', 'zod'], e) - expect(readFileSync(join(e.home, '.review', 'CORPUS.md'), 'utf8')).toContain('zod') // taste assembled - const s = read(e.settings) - expect(s.hooks.SessionStart[0].hooks[0].command).toContain('prime.ts') // and hook wired - clean(e) -}) - -test('status renders the latest sweep workflow from state/status.json', () => { - const e = env() - const stateDir = join(e.home, 'state') - mkdirSync(stateDir, { recursive: true }) - writeFileSync( - join(stateDir, 'status.json'), - JSON.stringify({ - version: 1, - repo: 'acme/widgets', - scope: 'auto', - dryRun: false, - stage: 'reviewing', - startedAt: '2026-06-22T10:00:00Z', - updatedAt: '2026-06-22T10:00:30Z', - message: 'reviewing 2 PR(s) in scope', - totals: { openPrs: 3, inScope: 2, handled: 1, reviewed: 0, skipped: 1, tokens: 0, maxPrs: 15 }, - prs: [ - { - number: 7, - title: 'tighten parser', - head: 'abcdef123456', - state: 'reviewing', - detail: 'running codex over 91 diff lines', - lines: 91, - updatedAt: '2026-06-22T10:00:30Z', - }, - { - number: 8, - title: 'huge import', - head: '999999999999', - state: 'skipped', - detail: 'diff 7000 lines > cap 5000', - lines: 7000, - updatedAt: '2026-06-22T10:00:20Z', - }, - ], - }), - ) - - const r = run(['status'], e) - expect(r.status).toBe(0) - expect(r.stdout).toContain('stupify status') - expect(r.stdout).toContain('acme/widgets') - expect(r.stdout).toContain('#7 tighten parser') - expect(r.stdout).toContain('running codex over 91 diff lines') - expect(r.stdout).toContain('#8 huge import') - clean(e) -}) diff --git a/src/prime.ts b/src/prime.ts index 8401000..855aa00 100644 --- a/src/prime.ts +++ b/src/prime.ts @@ -16,7 +16,7 @@ import { join } from 'node:path' const HOME = process.env.STUPIFY_HOME ?? join(homedir(), '.stupify') const BUDGET = 9000 // max bytes of injected additionalContext — measured: SessionStart silently truncates above ~10KB -/** Resolve taste like the reviewer does (the repo you're coding in wins, else the pack taste setup assembled) +/** Resolve taste like the reviewer does (the repo you're coding in wins, else the global one under ~/.stupify/.review) * and build the SessionStart payload. Returns null when no taste is set up — caller emits nothing. */ export function primePayload(cwd: string = process.cwd(), home: string = HOME): string | null { // A repo's .review/ lives at its git ROOT — so a session opened in a subdir still finds it (cwd → root → home). @@ -44,15 +44,15 @@ ${rubric} ## The code yours should look like — match it (CORPUS) ` // A SessionStart hook's additionalContext is silently truncated above the cap, and the corpus lands LAST. A - // multi-pack corpus easily exceeds the room — and a naive trim would keep only the FIRST pack and silently - // drop the rest. Instead, give every pack a FAIR SHARE of the room so each picked taste is represented (the - // reviewer reads the full CORPUS.md from disk and is unaffected). CORPUS.md is `intro --- pack1 --- pack2 …`; + // multi-section corpus easily exceeds the room — and a naive trim would keep only the FIRST section and silently + // drop the rest. Instead, give every section a FAIR SHARE of the room so each is represented (the + // reviewer reads the full CORPUS.md from disk and is unaffected). CORPUS.md is `intro --- section1 --- section2 …`; // a single-section corpus (e.g. from `stupify init`) degrades to a plain trim of that one section. const room = BUDGET - head.length if (corpus.length > room) { - const [intro = '', ...packs] = corpus.split('\n\n---\n\n') - const trimNote = '\n\n_(trimmed per pack to fit the session-start budget — full corpus in .review/CORPUS.md)_' - const per = Math.max(400, (room - intro.length - trimNote.length) / Math.max(1, packs.length)) + const [intro = '', ...sections] = corpus.split('\n\n---\n\n') + const trimNote = '\n\n_(trimmed per section to fit the session-start budget — full corpus in .review/CORPUS.md)_' + const per = Math.max(400, (room - intro.length - trimNote.length) / Math.max(1, sections.length)) const trimSection = (p: string) => { if (p.length <= per) { return p @@ -60,7 +60,7 @@ ${rubric} const cut = Math.max(p.lastIndexOf('\n### ', per), p.lastIndexOf('\n```\n', per)) // whole exemplars only return cut > 0 ? p.slice(0, cut) : p.slice(0, per) } - corpus = `${[intro, ...packs.map((p) => trimSection(p))].join('\n\n---\n\n')}${trimNote}` + corpus = `${[intro, ...sections.map((p) => trimSection(p))].join('\n\n---\n\n')}${trimNote}` } return JSON.stringify({ hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: head + corpus } }) } diff --git a/src/review-sweep.ts b/src/review-sweep.ts index 64a65f4..c5eea8d 100755 --- a/src/review-sweep.ts +++ b/src/review-sweep.ts @@ -83,7 +83,7 @@ async function main(): Promise { process.exit(1) } // Resolve the taste: the target repo's own .review/ wins (a repo can override); otherwise fall back to the - // home taste the CLI assembled from packs (~/.stupify/.review). Either way cfg.reviewDir becomes ABSOLUTE. + // global taste under ~/.stupify/.review. Either way cfg.reviewDir becomes ABSOLUTE. // Select on the FULL 3-file set, not just CORPUS.md — a partial repo .review/ (e.g. CORPUS without the spec) // then gracefully falls back to the home taste instead of being picked and dead-ending at "no machinery". setStatusStage(cfg, status, 'loading_taste', 'loading review taste') @@ -91,7 +91,7 @@ async function main(): Promise { cfg.reviewDir = hasMachinery(repoReview) ? repoReview : cfg.homeReviewDir if (!hasMachinery(cfg.reviewDir)) { log( - `no review machinery at ${cfg.reviewDir}/ (need REVIEW-PROMPT.md + RUBRIC.md + CORPUS.md) — no-op. Run \`stupify setup\` to assemble taste, or add a .review/ to ${cfg.slug}.`, + `no review machinery at ${cfg.reviewDir}/ (need REVIEW-PROMPT.md + RUBRIC.md + CORPUS.md) — no-op. Add a .review/ to ${cfg.slug} (\`stupify init\`).`, ) status.stage = 'done' status.message = 'no review machinery found' diff --git a/src/sweep/config.ts b/src/sweep/config.ts index 73a6308..b8e4e16 100644 --- a/src/sweep/config.ts +++ b/src/sweep/config.ts @@ -20,7 +20,7 @@ export const Config = z.object({ slug: z.string(), defaultBranch: z.string(), reviewDir: z.string(), // resolved later to an absolute path (repo .review/ or homeReviewDir) - homeReviewDir: z.string(), // fallback taste the CLI assembled under STUPIFY_HOME/.review + homeReviewDir: z.string(), // fallback global taste under STUPIFY_HOME/.review scope: Scope, reviewLabel: z.string(), diffLineCap: z.number(), diff --git a/src/sweep/review-one.ts b/src/sweep/review-one.ts index 7076ce1..3a1c31a 100644 --- a/src/sweep/review-one.ts +++ b/src/sweep/review-one.ts @@ -25,7 +25,7 @@ export async function reviewOne(cfg: Config, ref: string, post: boolean): Promis process.exit(1) } cfg.slug = slug - // Taste: this repo's own .review/ if you're standing in it, else the home taste the CLI assembled from packs. + // Taste: this repo's own .review/ if you're standing in it, else the global one under ~/.stupify/.review. const cwdReview = join(process.cwd(), '.review') cfg.reviewDir = hasMachinery(cwdReview) ? cwdReview : cfg.homeReviewDir if (!hasMachinery(cfg.reviewDir)) { diff --git a/src/sweep/verdict.test.ts b/src/sweep/verdict.test.ts deleted file mode 100644 index ea27d8d..0000000 --- a/src/sweep/verdict.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { expect, test } from 'bun:test' - -import { parseReview, parseReviewJson } from './verdict' - -test('parseReview stamps emoji, conf, and file pointer', () => { - const parsed = parseReview({ - verdict: 'findings', - opener: '', - findings: [{ path: 'src/x.ts', line: 30, severity: 'high', conf: 0.9, body: 'breaks on empty' }], - }) - if (parsed.kind !== 'findings') { - throw new Error('expected findings') - } - expect(parsed.findings[0]?.body).toBe(`🔴 · conf 0.9 · **\`src/x.ts:30\`** - -breaks on empty`) -}) - -test('parseReview keeps fixed and no_new_issues when findings are empty', () => { - expect(parseReview({ verdict: 'fixed', opener: '', findings: [] })).toEqual({ kind: 'fixed' }) - expect(parseReview({ verdict: 'no_new_issues', opener: '', findings: [] })).toEqual({ kind: 'no_new_issues' }) -}) - -test('parseReview rejects empty or contradictory findings', () => { - expect(() => parseReview({ verdict: 'findings', opener: '', findings: [] })).toThrow() - expect(() => - parseReview({ - verdict: 'fixed', - opener: '', - findings: [{ path: 'src/x.ts', line: 1, severity: 'low', conf: 0.1, body: 'leftover' }], - }), - ).toThrow() -}) - -test('parseReviewJson reads the second-pass message', () => { - const verdict = parseReviewJson( - JSON.stringify({ - verdict: 'findings', - opener: 'ok', - findings: [{ path: 'a.ts', line: 2, severity: 'med', conf: 1, body: 'dup' }], - }), - ) - if (verdict.kind !== 'findings') { - throw new Error('expected findings') - } - expect(verdict.opener).toBe('ok') - expect(verdict.findings[0]?.blocking).toBe(true) -})