tlon-skill: Migrate the full posts command family to the pure command runner#5922
Open
patosullivan wants to merge 1 commit into
Open
tlon-skill: Migrate the full posts command family to the pure command runner#5922patosullivan wants to merge 1 commit into
patosullivan wants to merge 1 commit into
Conversation
Base automatically changed from
po/tlon-5771-phase-m2-release-pipeline-port
to
develop
June 11, 2026 17:32
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This finishes moving the
tlon postscommand family off the legacy import-side-effect module and onto the injectablerun(args, deps)command contract.posts reactalready used the contract; this change migrates the remaining subcommands —unreact,delete,edit— plus the family shell (bareposts,posts --help, unknown subcommands, and the unsupportedsend/replyguidance). With the whole family on the contract, the legacyscripts/posts.tsmodule is deleted andscripts/main.tsroutes the entirepostsfamily through the runner with noprocess.argvmutation.User-facing behavior is unchanged: help text, usage errors, unsupported-command messages, success strings, and the post-id formatting pipeline are all preserved byte-for-byte. The win is testability and the removal of an auto-running command module.
What changed
scripts/commands/posts.ts— grew from the react-only slice into the full family runner. Parsing produces a discriminated result covering help,react,unreact,delete, andedit; bare/unknown invocations throw a usage error and unsupportedsend/replythrow a command error, all before authentication. The id helpers (extractNumericId/formatUd) stay self-contained, and the existing-post lookup (around-cursor query, exact-id match, null-on-error) lives here where it is directly testable. The module imports only pure helpers (markdownToStoryfrom./story); the command-contract guard continues to pass.scripts/posts-runtime.ts— the process-backed facade gainedremoveReaction,deletePost,editPost, and a thingetChannelPostslookup, plusnow()(clock) andreadFile()deps for edit. API failures are wrapped incommandErrorexactly as the react path already did.scripts/main.ts— thepostscase now callsrunPostsCommand(scriptArgs, createPostsDeps())for every subcommand; thereact-only special case, theprocess.argvrewrite, and theimport('./posts')branch are gone.scripts/posts.ts— deleted. Nothing imports it.scripts/cli-test-matrix.ts— added nested-help, missing-arg, auth-required, and unsupported-command cases for the newly migrated subcommands, and extendedHOSTILE_HELP_COMMANDSwithposts unreact,posts delete, andposts edit. These flow into both the source hermetic suite and the built-binary smoke automatically.Decision log (legacy quirks: preserved vs. tightened)
Every decision below preserves legacy behavior; each is covered by tests.
send/replyerrors (posts send --helpprints family help): this follows from parse order (help is resolved before the unsupported-command branch), but that specific combination is not separately pinned by a test.posts edit <ch> <id> --helptreats--helpas the edit message and reaches auth — it does not print help. Its near-twinposts edit <ch> <id> --title --helpdoes print edit help, because the message slice ends at the first option flag and the literal check never fires. Both adjacent cases are pinned in-process and as built-binary matrix cases.--title --image urltakes--imageas the title value. Preserved and tested.unreactanddelete(and the emoji forreact). Preserved and tested.--contentfile — so a nonexistent--contentfile fails withMissing Urbit configin a no-config environment, never a filesystem error. Pinned with an injected call-order assertion in-process and an auth-required case through the real binary.--contentread/parse failures are caught after auth and the existing-post lookup and rethrown as a stableError: <message>(exit 1, no stack/code frame). Both missing-file and invalid-JSON cases are tested.unreactcallsremoveReactionwithpostAuthorset to the current user;deletecallsdeletePostwith the formatted id and current user id;editmerges metadata (new--title/--imageoverride, existingtitle/image/description/coverpreserved, null lookup → undefined) and stampssentAtfrom the injected clock.✓ Reaction added,✓ Reaction removed,✓ Post edited,✓ Post deleted.Testing
pnpm --filter '@tloncorp/tlon-skill' check(typecheck + unit + hermetic + built-binary smoke from a fresh build) passes.pnpm -r tscpasses fortlon-skill.~ship/ids, exact success strings, payload shapes, edit metadata merge (found / null / overrides / failed lookup), content via message vs.--contentfile, message-slicing edges, content read/parse failures, the injected clock, the help-literal quirk, positional flag-value parsing, and expected-vs-unexpected error propagation.send/replyerrors.