Production-grade hardening: publishable packages, Changesets, CI matrix, coverage gate#1
Merged
Merged
Conversation
Captures the design for four scoped workstreams: 1. README positioning + tagline 2. Explicit error-code escape hatch (docs gap) 3. TaskResult production-grade pass (cancellation, pruning, docs) 4. ESLint plugin robustness via fixture corpus Sequenced 1 -> 2 -> 3 -> 4; small/safe items first.
30 bite-sized tasks across four phases: - Phase 1 (T1-2): README tagline + positioning - Phase 2 (T3-5): explicit error-code escape hatch docs + tests - Phase 3 (T6-12): TaskResult cancellation, pruning, fromPromise fix, docs, changelog - Phase 4 (T13-28): ESLint torture corpus, triage, three rule fixes, LIMITATIONS.md, changelog - Wrap-up (T29-30): green-build verification + PR
BREAKING CHANGE: TaskResult.toPromise() has been removed. Call .run() instead — it has the same signature and behavior.
Previously, a rejection from the factory bubbled up as an uncaught promise rejection. Now it's wrapped as System.Unexpected with the original throw preserved as .cause. BREAKING CHANGE: fromPromise return type now widens to include System.Unexpected.
Records expected behavior. Triage results follow in Task 23.
Replace the three `callSigs[0]!` sites in throw-type-mismatch with a union loop over all signatures so overloaded functions are fully inspected. Update the overloads fixture to use TypedPromise return types and expect a transitiveError report for the undeclared E.B error that was previously invisible.
…scope Add Identifier to the undeclaredThrow filter in ThrowStatement handler. Previously, `throw someVar` where the variable's type was unknown/any was silently ignored even inside a TypedPromise-returning function. Now reports undeclaredThrow for Identifier throws alongside NewExpression and Literal. Adds variable-throw torture fixture demonstrating the blind spot.
… try-block throws Throws inside async function literals (e.g. setTimeout callbacks, .map handlers) are wrapped in rejected promises and cannot synchronously escape to the enclosing catch block. Previously, walkNode would descend into these async closures and misattribute their TypedPromise-annotated calls as throws that the outer catch must handle. Fix: update walkNode's callback signature to support a boolean skip signal (return false halts descent), then return false when the try-block walk encounters an async FunctionExpression or ArrowFunctionExpression. Synchronous inline lambdas are intentionally NOT skipped — their throws can propagate synchronously (see the generics fixture). Adds a forward-looking torture fixture (nested-try case 3) that confirms async-closure throws are not attributed to the outer catch.
- build to dist/ via tsdown (ESM, shebang preserved, typescript external) - bin -> ./dist/index.js with #!/usr/bin/env node (was raw .ts + bun shebang) - declare typescript as a runtime dependency; drop unused faultline dep - add files, repository, homepage, author, keywords, engines, publishConfig - add README and LICENSE
- add README.md (was referenced by files field but missing -> broken publish) - inline meta.version from package.json at build time (was hardcoded 0.1.0, drifted from 0.2.0) - run tests in prepublishOnly (was typecheck + build only)
…gate - result.test.ts: every Ok/Err method, match/catchTag/all combinators (result.ts 47%->100% funcs) - attempt.test.ts: async mapUnknown/mapAbort, pre-abort race, happy paths - from-unknown.test.ts: full context frames, AppError passthrough, detail preservation - serialize.test.ts: standalone AppError JSON.stringify uses toJSON - bunfig.toml: enforce 90% line/function coverage gate
- add @changesets/cli + .changeset config (public access, vscode ignored) - rewrite release.yml: Changesets action opens a version PR, then publishes to npm with provenance (replaces manual tag-triggered publish) - root scripts: changeset, version-packages, release (builds the 3 publishable packages then changeset publish) - commit bun.lock (was gitignored despite CI using --frozen-lockfile) - mark faultline-vscode private (ships to VS Code marketplace, not npm) - changesets capturing unreleased work: core TaskResult changes (minor), eslint-plugin rule fixes + packaging (minor), CLI publishability (minor) - root CHANGELOG now points to per-package changelogs owned by Changesets
- quality job now builds faultline + eslint-plugin + cli (was faultline only) - test step enforces the coverage gate - new node-compat matrix builds artifacts and smoke-loads them under real Node 18/20/22: faultline CJS+ESM, eslint-plugin require, cli bin execution - keeps the are-the-types-wrong export validation for faultline
- README: npm/CI/license/zero-deps badges, Contributing section with package status table and dev commands, mark VS Code extension experimental - CONTRIBUTING.md: monorepo workflow, test/coverage/changeset expectations - SECURITY.md: private vulnerability reporting policy - CODE_OF_CONDUCT.md: Contributor Covenant 2.1 - issue forms (bug/feature) + config, pull request template
Addresses sharp edges found while adversarially pressure-testing: - redaction: add '**' globstar segment so a sensitive key can be redacted at any depth (e.g. '**.password' covers root, nested objects, and array elements). redactPaths fail open (a non-matching path silently redacts nothing), so '**.<key>' is now the recommended form; documented in README and SECURITY.md. Previously a naive ['password'] config leaked everything. - attemptAsync/TaskResult: document that cancellation is cooperative — it short-circuits subsequent steps and stops the caller waiting, but does not preempt in-flight work unless the function threads the signal through. - defineBoundary: document that it throws System.BoundaryViolation on an unmapped tag and overwrites a handler-set cause (both already tested). Behavior change is additive (new '**' wildcard); the rest is docs.
…to 100% - defineBoundary: add boundary.try(error) returning Result<To, BoundaryViolation> instead of throwing on an unmapped tag — safe to use at a transport edge. Shares mapping logic with the throwing call so behavior stays in lock-step. Exposes the BoundaryViolationError type. - task-result: tests for andThen (ok/err/short-circuit/async), catchTag with a TaskResult-returning handler, and the instance match() — task-result.ts now 100% line/function coverage.
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.
Brings faultline to a production-grade, trustworthy state across packaging, CI, tests, release process, docs, and a round of adversarial hardening.
Packaging & publishability
dist/via tsdown,#!/usr/bin/env nodebin,typescriptas a runtime dep,files/metadata/README/LICENSE).README.md(was referenced byfiles→ broken publish),meta.versionnow inlined frompackage.jsonat build time (no more drift), tests run inprepublishOnly.Release process — Changesets
release.ymlrewritten (version PR → publish). VS Code ignored.bun.lock(was gitignored despite CI using--frozen-lockfile).CI
require, CLI bin execution).Tests & coverage
bunfig.toml.result.ts47%→100%,task-result.ts→100%, dedicatedattempt/from-unknownsuites. ~196 core tests.Adversarial hardening (pressure test)
Built a realistic service and tried to break the thin paths. Core held up (no crashes/corruption/injection, 25k-deep chains, graceful on untrusted input). Fixed the sharp edges found:
**globstar so**.passwordredacts a key at any depth; documented that paths fail open. Previously a naive['password']config silently leaked everything.Result<To, BoundaryViolation>for use at a transport edge.Repo hygiene
CONTRIBUTING, SECURITY, CODE_OF_CONDUCT, issue forms + PR template, README badges + Contributing/packages section.
Notes for the reviewer
faultline+eslint-plugin-faultline(0.3.0) and the firstfaultline-cli0.1.0.npmenvironment +NPM_TOKENsecret (already referenced by the prior workflow).All green locally: typecheck, ~216 tests across packages, lint, build, Node smoke tests.