| name | three-path-wire |
| description | Wire up the three-path structure for a function. Ensures Maybe uses chain, Result/Validation use chainWiden, and Validation accumulates all errors. PREREQUISITE - help-system-prep must be complete for this function. Work on ONE function at a time. |
Three Path Wire
Wire up the three-path structure for a function. This skill assumes help-system-prep is complete for this function.
MANDATORY: Follow ALL Rules
READ AND FOLLOW docs/THESE_ARE_THE_RULES.md WITHOUT EXCEPTION.
WE DO NOT ALLOW, UNDER ANY CIRCUMSTANCES, ANY TECH DEBT IN THIS LIBRARY.
Prerequisite
help-system-prep must be complete for this function. The following must exist and be canonical:
constants/index.tswith code strings onlytypes/index.tswith HelpCode type- All
_*Help/functions with pipeHelp pattern
Files This Skill Touches
_functionNameToMaybe/index.ts_functionNameToMaybe/_processData/index.ts_functionNameToMaybe/_processData/index.test.ts_functionNameToMaybe/index.test.ts_functionNameToResult/index.ts_functionNameToResult/_processData/index.ts_functionNameToResult/_processData/index.test.ts_functionNameToResult/index.test.ts_functionNameToValidation/index.ts_functionNameToValidation/_processData/index.ts_functionNameToValidation/_processData/index.test.ts_functionNameToValidation/index.test.tsindex.ts(main function)index.test.ts(main function tests)
Files This Skill Does NOT Touch
constants/index.tstypes/index.ts_*Help/functions
Rules Enforced
Rule 1: Maybe Routing Uses chain
File: _functionNameToMaybe/index.ts
import type { Maybe } from "path/to/monads/maybe/types/index.ts"
import chain from "path/to/monads/maybe/chain/index.ts"
import _processData from "./_processData/index.ts"
//++ Private helper for functionName on Maybe monads
export default function _functionNameToMaybe<T>(
maybeData: Maybe<DataType>,
): Maybe<OutputType> {
return chain<DataType, OutputType>(_processData<T>)(maybeData)
}
Rule 2: Result/Validation Routing Uses chainWiden
File: _functionNameToResult/index.ts
import type { BaseHelp } from "path/to/help/types/index.ts"
import type { Result } from "path/to/monads/result/types/index.ts"
import chainWiden from "path/to/monads/result/chainWiden/index.ts"
import type { FunctionNameHelpCode } from "../types/index.ts"
import _processData from "./_processData/index.ts"
//++ Private helper for functionName on Result monads
export default function _functionNameToResult<T, H>(
result: Result<H, DataType>,
): Result<H | BaseHelp<FunctionNameHelpCode>, OutputType> {
return chainWiden<DataType, OutputType, BaseHelp<FunctionNameHelpCode>>(
_processData<T>,
)(result)
}
Same pattern for Validation with chainWiden.
Note: FunctionNameHelpCode is the code string union. BaseHelp<FunctionNameHelpCode> is the full help object type.
Rule 3: Curry Levels
All Toolsmith functions are curried — exactly one parameter per function level.
1 curry level (data only):
export default function functionName<T, H>(
data: Maybe<DataType> | Result<H, DataType> | Validation<H, DataType>,
): ...
2 curry levels (one param + data):
export default function functionName<T, H>(param1: Param1Type) {
function functionNameWithParam1(
data: Maybe<DataType> | Result<H, DataType> | Validation<H, DataType>,
): ... {
// implementation
}
return functionNameWithParam1
}
3 curry levels (two params + data):
export default function functionName<T, H>(param1: Param1Type) {
return function functionNameWithParam1(param2: Param2Type) {
function functionNameWithParam1AndParam2(
data: Maybe<DataType> | Result<H, DataType> | Validation<H, DataType>,
): ... {
// implementation
}
return functionNameWithParam1AndParam2
}
}
Rule 4: Overloads on Innermost Function
Overloads go on the function that takes the monad-wrapped data.
- 1 curry level: Overloads on main function
- 2+ curry levels: Overloads on inner function
Rule 5: _processData for Maybe
Returns Maybe<OutputType>. Returns nothing() for all failures.
export default function _processData<T>(param: ParamType) {
return function _processDataWithParam(data: DataType): Maybe<OutputType> {
if (isValidParam(param)) {
if (isValidData(data)) {
return just(result)
}
}
return nothing()
}
}
Rule 6: _processData for Result
Returns Result<BaseHelp<FunctionNameHelpCode>, OutputType>. Uses help() with extracted help functions.
import type { BaseHelp } from "path/to/help/types/index.ts"
import type { FunctionNameHelpCode } from "../../types/index.ts"
export default function _processData<T>(param: ParamType) {
return function _processDataWithParam(
data: DataType,
): Result<BaseHelp<FunctionNameHelpCode>, OutputType> {
if (isValidParam(param)) {
if (isValidData(data)) {
return ok(result)
}
return help(_dataNotUnderstoodHelp(data))
}
return help(_paramNotUnderstoodHelp(param))
}
}
Rule 7: _processData for Validation — ACCUMULATES ERRORS
Returns Validation<BaseHelp<FunctionNameHelpCode>, OutputType>. Uses failure([]) with array. MUST accumulate ALL errors when multiple params fail.
import type { BaseHelp } from "path/to/help/types/index.ts"
import type { FunctionNameHelpCode } from "../../types/index.ts"
export default function _processData<T>(param: ParamType) {
return function _processDataWithParam(
data: DataType,
): Validation<BaseHelp<FunctionNameHelpCode>, OutputType> {
if (isValidParam(param)) {
if (isValidData(data)) {
return success(result)
}
return failure([_dataNotUnderstoodHelp(data)])
}
// param is invalid — check if data is also invalid
if (isValidData(data)) {
return failure([_paramNotUnderstoodHelp(param)])
}
// BOTH invalid — accumulate both helps
return failure([
_paramNotUnderstoodHelp(param),
_dataNotUnderstoodHelp(data),
])
}
}
Accumulation with 2 params + data (3 curry levels):
- param1 bad, param2 good, data good → 1 help
- param1 good, param2 bad, data good → 1 help
- param1 good, param2 good, data bad → 1 help
- param1 bad, param2 bad, data good → 2 helps
- param1 bad, param2 good, data bad → 2 helps
- param1 good, param2 bad, data bad → 2 helps
- param1 bad, param2 bad, data bad → 3 helps
Rule 8: Function Parameters Require try/catch
If a parameter is a user-provided function (predicate, transformer, reducer), wrap the operation in try/catch.
/*++
+ [EXCEPTION] try/catch permitted to wrap user-provided function
+ [description of what function does]
*/
export default function _processData<T>(
fn: (value: T) => boolean,
) {
return function _processDataWithFn(
data: DataType,
): Maybe<OutputType> {
try {
if (isFunction(fn)) {
if (isValidData(data)) {
return just(data.someMethod(fn))
}
}
return nothing()
} catch (_err) {
return nothing()
}
}
}
For Result/Validation, catch returns help/failure with _functionFumbledHelp(err).
Rule 9: Main index.ts Uses _routeThreePaths
import type { BaseHelp } from "path/to/help/types/index.ts"
import type { FunctionNameHelpCode } from "./types/index.ts"
export default function functionName<T, H>(param: ParamType) {
// overloads here for 2+ curry levels
function functionNameWithParam(
data: Maybe<DataType> | Result<H, DataType> | Validation<H, DataType>,
):
| Maybe<OutputType>
| Result<H | BaseHelp<FunctionNameHelpCode>, OutputType>
| Validation<H | BaseHelp<FunctionNameHelpCode>, OutputType> {
return _routeThreePaths<H, BaseHelp<FunctionNameHelpCode>, DataType, OutputType>({
toMaybe: _functionNameToMaybe<T>(param),
toResult: _functionNameToResult<T, H>(param),
toValidation: _functionNameToValidation<T, H>(param),
})(data)
}
return functionNameWithParam
}
Workflow
For each function (one at a time):
- Verify help-system-prep is complete (constants, types, help functions exist and are canonical)
- Read all routing and processing files to understand current state
- Maybe path (complete before moving to Result):
- Fix
_functionNameToMaybe/_processData/index.ts - Run
deno task fmt && deno task lint - Run
deno checkon the file - Fix
_functionNameToMaybe/_processData/index.test.ts - Run tests, verify 100% coverage
- Fix
_functionNameToMaybe/index.ts - Run
deno task fmt && deno task lint - Run
deno checkon the file - Fix
_functionNameToMaybe/index.test.ts - Run tests, verify 100% coverage
- Fix
- Result path (complete before moving to Validation):
- Same workflow as Maybe path
- Validation path (complete before moving to main):
- Same workflow as Maybe path
- VERIFY accumulation logic is correct
- Main function:
- Fix
index.ts - Run
deno task fmt && deno task lint - Run
deno check index.ts - Fix
index.test.ts - Run all tests for the function
- Verify 100% coverage on all files
- Fix
- Only then move to next function
Completion Criteria
A function is NOT complete until ALL of these are true:
- Maybe routing uses
chain - Result routing uses
chainWiden - Validation routing uses
chainWiden - All
_processDatafunctions use extracted help functions - Validation
_processDataaccumulates ALL errors (all combinations tested) - Function params have try/catch wrapper with [EXCEPTION] comment
- Main
index.tsuses_routeThreePaths - Overloads on correct function (innermost that takes monad)
- All curry levels have exactly one parameter
-
deno task lintpasses with zero issues -
deno checkpasses on all files - 100% test coverage on all files
Do not move to the next function until all criteria are met.