{"_id":"@akshatmittal/invoker","_rev":"6-7aafd09a386a16a8f0e7c5f74e75a605","name":"@akshatmittal/invoker","dist-tags":{"latest":"0.4.1"},"versions":{"0.1.0":{"name":"@akshatmittal/invoker","version":"0.1.0","keywords":["matrix","regression","vitest","workflow"],"license":"MIT","_id":"@akshatmittal/invoker@0.1.0","maintainers":[{"name":"akshatmittal","email":"hi@akshatmittal.com"}],"dist":{"shasum":"3656987150a061b8425fabf72a6177b2721f4b06","tarball":"https://registry.npmjs.org/@akshatmittal/invoker/-/invoker-0.1.0.tgz","fileCount":5,"integrity":"sha512-5sg5Y0IgHEEunaXlPqCKWr6obwrPlbuBG3c6Sf4VoJpOUY3+VGeMTwiLLvzsc1hcgiiKU6tEexxrKGG63Eaymw==","signatures":[{"sig":"MEUCIBPb3UrIU14sU1I2VvTvy3OfvtH6RoAg7G6e1gTNAdsrAiEAkMcK01lb3tGb5/Ncuopx4klP9rlYwFx8oa/vc505Fac=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":15928},"main":"./dist/index.mjs","type":"module","types":"./dist/index.d.mts","module":"./dist/index.mjs","engines":{"node":"^24.18.1"},"exports":{".":{"types":"./dist/index.d.mts","import":"./dist/index.mjs"}},"scripts":{"dev":"tsdown --watch","build":"tsdown","typecheck":"tsc --noEmit"},"_npmUser":{"name":"akshatmittal","email":"hi@akshatmittal.com"},"repository":{"url":"https://github.com/akshatmittal/invoker.git","type":"git"},"description":"Typed matrix-driven regression workflows for Vitest.","directories":{},"sideEffects":false,"_nodeVersion":"24.18.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsdown":"^0.22.14","vitest":"^4.1.10","typescript":"^7.0.2","@types/node":"^24.13.3","@workspace/tsconfig":"0.0.0"},"peerDependencies":{"vitest":"^4.1.10"},"_npmOperationalInternal":{"tmp":"tmp/invoker_0.1.0_1786529954709_0.6864964616116971","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@akshatmittal/invoker","version":"0.2.0","keywords":["matrix","regression","vitest","workflow"],"license":"MIT","_id":"@akshatmittal/invoker@0.2.0","maintainers":[{"name":"akshatmittal","email":"hi@akshatmittal.com"}],"dist":{"shasum":"acfbcd557a8d21a55bc38501f41f5996c8791eae","tarball":"https://registry.npmjs.org/@akshatmittal/invoker/-/invoker-0.2.0.tgz","fileCount":9,"integrity":"sha512-tuIjFB05845P5u7KerZtUaPdaVznlbA1YuM86sKTRIhEOcIyDXCQJw/ghqrlAJY/VR5NAGei5NtCn/kHLurMng==","signatures":[{"sig":"MEYCIQClWq0S3swOvkRgtMSby85/e5U4Han0B2xFsJ+onAw+BgIhANMNY1oT3M/rcsqzlx5S6wGnFAKrUPUMh4IzRthuPQb4","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akshatmittal%2finvoker@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":47608},"main":"./dist/index.mjs","type":"module","types":"./dist/index.d.mts","module":"./dist/index.mjs","engines":{"node":"^24.18.1"},"exports":{".":{"types":"./dist/index.d.mts","import":"./dist/index.mjs"},"./slack":{"types":"./dist/slack.d.mts","import":"./dist/slack.mjs"},"./github":{"types":"./dist/github.d.mts","import":"./dist/github.mjs"}},"scripts":{"dev":"tsdown --watch","build":"tsdown","typecheck":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:d2dec269-0103-42e0-bff1-c83afe4864e7"}},"repository":{"url":"https://github.com/akshatmittal/invoker.git","type":"git"},"description":"Typed matrix-driven regression workflows for Vitest.","directories":{},"sideEffects":false,"_nodeVersion":"26.0.0","dependencies":{"zod":"^4.4.3","evlog":"^2.26.0","croner":"^10.0.1","@slack/web-api":"^8.0.0","@octokit/request":"^10.0.13","@octokit/auth-app":"^8.3.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsdown":"^0.22.14","vitest":"^4.1.10","typescript":"^7.0.2","@types/node":"^24.13.3","@workspace/tsconfig":"0.0.0"},"peerDependencies":{"vitest":"^4.1.10"},"peerDependenciesMeta":{"vitest":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/invoker_0.2.0_1787678080627_0.7709090700748238","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@akshatmittal/invoker","version":"0.3.0","keywords":["matrix","regression","vitest","workflow"],"license":"MIT","_id":"@akshatmittal/invoker@0.3.0","maintainers":[{"name":"akshatmittal","email":"hi@akshatmittal.com"}],"dist":{"shasum":"8284b320470614e912bc96f0cc26f7969fb863a5","tarball":"https://registry.npmjs.org/@akshatmittal/invoker/-/invoker-0.3.0.tgz","fileCount":9,"integrity":"sha512-uq1ZD3ccmLy0VADZJmW7darAbpcG22gaV2N8dGCZwuJUT/QS6AefkATIXeMKrqYl4kKTwFH7iUJAwIrCLjC39g==","signatures":[{"sig":"MEQCICcZshtHZqsGrJu+qT8zOPDYF3zgoU1hRUnS1gqIi82LAiAVDm/t4C7oZPUpdc1+lZvm4KeKnhYH1XLPWXGRmM7yMA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQCh1zbbv9hO6eIiHk6I2gKh0lOfexnYqEbbITzjbG85cAIgM0QNUDiCUOmRI8RiXDc/IodZVxvFwkihFugJR9Hz2Qk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akshatmittal%2finvoker@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":49551},"main":"./dist/index.mjs","type":"module","types":"./dist/index.d.mts","module":"./dist/index.mjs","engines":{"node":"^24.18.1"},"exports":{".":{"types":"./dist/index.d.mts","import":"./dist/index.mjs"},"./slack":{"types":"./dist/slack.d.mts","import":"./dist/slack.mjs"},"./github":{"types":"./dist/github.d.mts","import":"./dist/github.mjs"}},"scripts":{"dev":"tsdown --watch","build":"tsdown","typecheck":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:d2dec269-0103-42e0-bff1-c83afe4864e7"}},"repository":{"url":"https://github.com/akshatmittal/invoker.git","type":"git"},"description":"Typed matrix-driven regression workflows for Vitest.","directories":{},"sideEffects":false,"_nodeVersion":"26.0.0","dependencies":{"zod":"^4.4.3","evlog":"^2.26.0","croner":"^10.0.1","@slack/web-api":"^8.0.0","@octokit/request":"^10.0.13","@octokit/auth-app":"^8.3.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsdown":"^0.22.14","vitest":"^4.1.10","typescript":"^7.0.2","@types/node":"^24.13.3","@workspace/tsconfig":"0.0.0"},"peerDependencies":{"vitest":"^4.1.10"},"peerDependenciesMeta":{"vitest":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/invoker_0.3.0_1788297031099_0.20631647683632748","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"name":"@akshatmittal/invoker","version":"0.3.1","keywords":["matrix","regression","vitest","workflow"],"license":"MIT","_id":"@akshatmittal/invoker@0.3.1","maintainers":[{"name":"akshatmittal","email":"hi@akshatmittal.com"}],"dist":{"shasum":"84091c1a3833109b601f933ad98d76ef7de3afb5","tarball":"https://registry.npmjs.org/@akshatmittal/invoker/-/invoker-0.3.1.tgz","fileCount":9,"integrity":"sha512-U1A28M7LLWAUXBrTC9UwhHJ2/EdP56ZHIJ9ltuBOcA0BtIkI0kniApKMFV5GTBJ/Wb4FBtq3ijwpCRBI3TTcrg==","signatures":[{"sig":"MEYCIQCjKONa43S4dEqbDApmKDuobBLJOkRZto5JTQp2H0kU3gIhALi8JhbFKJxFB+rjX4bIV9WmsD2Eh6/X4zxSO0NCylf7","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIH43NFkH9B40gpQ2OnDy3oDzxhOfQeGgTtyxYG07WvtMAiEAjWBxLpZR9jL9Ry4ACjjhVpd8Kd28ThDDrATYtrDhrjc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akshatmittal%2finvoker@0.3.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":51134},"main":"./dist/index.mjs","type":"module","types":"./dist/index.d.mts","module":"./dist/index.mjs","engines":{"node":"^24.18.1"},"exports":{".":{"types":"./dist/index.d.mts","import":"./dist/index.mjs"},"./slack":{"types":"./dist/slack.d.mts","import":"./dist/slack.mjs"},"./github":{"types":"./dist/github.d.mts","import":"./dist/github.mjs"}},"scripts":{"dev":"tsdown --watch","build":"tsdown","typecheck":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:d2dec269-0103-42e0-bff1-c83afe4864e7"}},"repository":{"url":"https://github.com/akshatmittal/invoker.git","type":"git"},"description":"Typed matrix-driven regression workflows for Vitest.","directories":{},"sideEffects":false,"_nodeVersion":"26.0.0","dependencies":{"zod":"^4.4.3","evlog":"^2.26.0","croner":"^10.0.1","@slack/web-api":"^8.0.0","@octokit/request":"^10.0.13","@octokit/auth-app":"^8.3.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsdown":"^0.22.14","vitest":"^4.1.10","typescript":"^7.0.2","@types/node":"^24.13.3","@workspace/tsconfig":"0.0.0"},"peerDependencies":{"vitest":"^4.1.10"},"peerDependenciesMeta":{"vitest":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/invoker_0.3.1_1788305723525_0.21843877937114708","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@akshatmittal/invoker","version":"0.4.0","keywords":["matrix","regression","vitest","workflow"],"license":"MIT","_id":"@akshatmittal/invoker@0.4.0","maintainers":[{"name":"akshatmittal","email":"hi@akshatmittal.com"}],"dist":{"shasum":"87427301538aadae6191988ead698596f6c4c8f4","tarball":"https://registry.npmjs.org/@akshatmittal/invoker/-/invoker-0.4.0.tgz","fileCount":10,"integrity":"sha512-TRO5yMWU8H2wn23Jty0SwRJDxDiTRf77HewuIWBbaUHOuoQ7VGIoFVXapIfCjSOt4dlfRKgYhhqcVcsP/YwPVg==","signatures":[{"sig":"MEQCIHU6rfJzhoPbTb/u2xx+K4DnZZJHq8GnGl5XTfIlnpFmAiBOIIX3c9f0D9ljP4vxaO4gXizaV5wRKFJxhzlKXewJqQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEYCIQD6ZKG8UklprVX46VTtl5j/4J/g47Nv2phy3D6dKt8fOgIhAMbYmFOiscmhWgXRfEoR3Y5BqKkUHhr7/c8AveaVFNFf","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akshatmittal%2finvoker@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":55320},"main":"./dist/index.mjs","type":"module","types":"./dist/index.d.mts","module":"./dist/index.mjs","engines":{"node":"^24.18.1"},"exports":{".":{"types":"./dist/index.d.mts","import":"./dist/index.mjs"},"./slack":{"types":"./dist/slack.d.mts","import":"./dist/slack.mjs"},"./github":{"types":"./dist/github.d.mts","import":"./dist/github.mjs"}},"scripts":{"dev":"tsdown --watch","build":"tsdown","typecheck":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:d2dec269-0103-42e0-bff1-c83afe4864e7"}},"repository":{"url":"https://github.com/akshatmittal/invoker.git","type":"git"},"description":"Typed matrix-driven regression workflows for Vitest.","directories":{},"sideEffects":false,"_nodeVersion":"26.0.0","dependencies":{"zod":"^4.4.3","evlog":"^2.26.0","croner":"^10.0.1","@slack/web-api":"^8.0.0","@octokit/request":"^10.0.13","@octokit/auth-app":"^8.3.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsdown":"^0.22.14","vitest":"^4.1.10","typescript":"^7.0.2","@types/node":"^24.13.3","@workspace/tsconfig":"0.0.0"},"peerDependencies":{"vitest":"^4.1.10"},"peerDependenciesMeta":{"vitest":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/invoker_0.4.0_1788896192543_0.9856969576642369","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"_id":"@akshatmittal/invoker@0.4.1","dist":{"shasum":"693d233165137a88fb6650f0a8bf3e2133d5f0a6","tarball":"https://registry.npmjs.org/@akshatmittal/invoker/-/invoker-0.4.1.tgz","fileCount":10,"integrity":"sha512-o1evS8TgBZC8qDZ4Z/UFOXfhYrGUpt6JZQBBl4a3JpbpTICdab1NxppfUfLDMefKCMM//H3chYa+OfN2uWY7og==","signatures":[{"sig":"MEUCIQDoiUePe3hbpNs7qCHq9Nd0zNhw00IxFot752AtsGt5IgIgOeIoi6CFvU/02pi+sLBgGnVEiFvq2xn1aTpRoFwZ4Tg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIGJ6RfcwhVllJCFVrDLBu3MfRZHKSHLCtzSha6H7OcAKAiEA5q5zZNpBavOuzCtuYimXLft3nsMNB4Hl6DA8ZVRh5WQ="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akshatmittal%2finvoker@0.4.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":55676},"main":"./dist/index.mjs","name":"@akshatmittal/invoker","type":"module","types":"./dist/index.d.mts","module":"./dist/index.mjs","engines":{"node":"^24.18.1"},"exports":{".":{"types":"./dist/index.d.mts","import":"./dist/index.mjs"},"./slack":{"types":"./dist/slack.d.mts","import":"./dist/slack.mjs"},"./github":{"types":"./dist/github.d.mts","import":"./dist/github.mjs"}},"license":"MIT","scripts":{"dev":"tsdown --watch","build":"tsdown","typecheck":"tsc --noEmit"},"version":"0.4.1","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:d2dec269-0103-42e0-bff1-c83afe4864e7"}},"keywords":["matrix","regression","vitest","workflow"],"repository":{"url":"https://github.com/akshatmittal/invoker.git","type":"git"},"description":"Typed matrix-driven regression workflows for Vitest.","directories":{},"maintainers":[{"name":"akshatmittal","email":"hi@akshatmittal.com"}],"sideEffects":false,"_nodeVersion":"26.0.0","dependencies":{"zod":"^4.4.3","evlog":"^2.26.0","croner":"^10.0.1","@slack/web-api":"^8.0.0","@octokit/request":"^10.0.13","@octokit/auth-app":"^8.3.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsdown":"^0.22.14","vitest":"^4.1.10","typescript":"^7.0.2","@types/node":"^24.13.3","@workspace/tsconfig":"0.0.0"},"peerDependencies":{"vitest":"^4.1.10"},"peerDependenciesMeta":{"vitest":{"optional":true}},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/invoker_0.4.1_1789851337308_0.08276297795087229"}}},"time":{"created":"2026-08-12T10:19:14.487Z","modified":"2026-09-19T20:55:37.716Z","0.1.0":"2026-08-12T10:19:14.910Z","0.2.0":"2026-08-25T17:14:40.784Z","0.3.0":"2026-09-01T21:10:31.192Z","0.3.1":"2026-09-01T23:35:23.614Z","0.4.0":"2026-09-08T19:36:32.631Z","0.4.1":"2026-09-19T20:55:37.403Z"},"license":"MIT","keywords":["matrix","regression","vitest","workflow"],"repository":{"url":"https://github.com/akshatmittal/invoker.git","type":"git"},"description":"Typed matrix-driven regression workflows for Vitest.","maintainers":[{"name":"akshatmittal","email":"hi@akshatmittal.com"}],"readme":"# @akshatmittal/invoker\n\nA strictly typed TypeScript DSL for matrix-driven regression workflows. Invoker\nexpands Tasks into Vitest tests, runs each Task's Cases concurrently, and stores\nvalidated JSON Output in Vitest metadata for reporters and later analysis.\n\n## Install\n\n```sh\npnpm add -D @akshatmittal/invoker vitest\n```\n\nInvoker supports Node 24.18.1 or newer within Node 24 and Vitest 4.1.10 or newer within Vitest 4.\n\n## Schedule GitHub Actions\n\n`@akshatmittal/invoker/github` runs code-defined GitHub Actions schedules from\na small, long-running Node process. It is independent from the Vitest SDK, so a\nscheduler-only installation does not need Vitest.\n\nCreate a GitHub App with repository **Actions: read and write**, disable its\nwebhook, install it on the selected repositories, and generate a private key.\nNo other repository, organization, user, OAuth, or webhook permissions are\nneeded.\n\nInstall the scheduler with t3-env and Zod in a plain ESM application:\n\n```sh\nnpm install @akshatmittal/invoker @t3-oss/env-core zod\n```\n\n```js\n// schedule.mjs\nimport { createEnv } from \"@t3-oss/env-core\";\nimport { defineGitHubSchedule } from \"@akshatmittal/invoker/github\";\nimport { z } from \"zod\";\n\nconst env = createEnv({\n  server: {\n    GITHUB_APP_ID: z.coerce.number().int().positive().max(Number.MAX_SAFE_INTEGER),\n    GITHUB_APP_PRIVATE_KEY: z.string().min(1),\n  },\n  runtimeEnv: process.env,\n});\n\nawait defineGitHubSchedule({\n  app: {\n    id: env.GITHUB_APP_ID,\n    privateKey: env.GITHUB_APP_PRIVATE_KEY,\n  },\n  schedules: [\n    {\n      cron: \"0 9 * * 1\",\n      timezone: \"UTC\",\n      repository: \"acme/regressions\",\n      workflow: \"invoker.yml\",\n      ref: \"main\",\n      inputs: { dataset: \"weekly\", publish: true },\n    },\n  ],\n});\n```\n\nSchedules use five-field cron expressions and default to UTC when `timezone`\nis omitted. Configuration is fixed at startup. Every repository installation\nand active workflow is validated before timers begin; GitHub validates the ref,\n`workflow_dispatch` declaration, and input schema when a Dispatch is due.\n\nRun exactly one replica. Dispatches may overlap, are not persisted or retried,\nand failures do not stop later occurrences. The process emits events through\nevlog's shared logger, so the host's filtering, sampling, redaction, and drain\nconfiguration applies. The module does not initialize or configure evlog.\n`SIGINT` and `SIGTERM` stop new Dispatches, await in-flight requests, and\nresolve the long-running promise.\n\n### Docker\n\nKeep `package.json`, `package-lock.json`, and `schedule.mjs` in a deployment\ndirectory and build this image:\n\n```dockerfile\nFROM node:24-slim\n\nWORKDIR /app\nCOPY package.json package-lock.json ./\nRUN npm ci --omit=dev\nCOPY schedule.mjs ./\n\nUSER node\nCMD [\"node\", \"schedule.mjs\"]\n```\n\nExclude `.env`, PEM, and private-key files from the build context. Inject\n`GITHUB_APP_ID` and the real multiline `GITHUB_APP_PRIVATE_KEY` through the\nruntime platform's secret mechanism; never bake them into the image. Use the\nplatform's process-liveness check and restart policy with one replica. The\nscheduler intentionally has no HTTP health endpoint.\n\n## Define a Workflow\n\nDeclare a Task's required parameters once, then bind them from each Workflow.\nTypeScript checks the bindings while inferring the Task's Matrix, setup, and Output:\n\n```ts\n// regressions/model/tasks/evaluate-models.ts\nimport { defineTask } from \"@akshatmittal/invoker\";\n\ntype EvaluateParams = {\n  environment: \"staging\" | \"production\";\n  baseline: string;\n};\n\nexport const evaluateModels = defineTask<EvaluateParams>()({\n  name: \"evaluate-models\",\n  matrix: async ({ params }) => ({\n    model: await discoverModels(params.environment),\n    dataset: [\"support\", \"sales\"],\n  }),\n  setup: async ({ params, cases }) => loadFixtures(params.environment, cases),\n  run: async ({ params, matrix, setup, vitest }) => {\n    vitest.expect(setup.has(matrix.dataset)).toBe(true);\n    return {\n      baseline: params.baseline,\n      score: await evaluate(matrix, setup),\n    };\n  },\n  teardown: async ({ setup }) => {\n    await setup.close();\n  },\n});\n```\n\n```ts\n// regressions/workflows/model-regressions.test.ts\nimport { defineWorkflow } from \"@akshatmittal/invoker\";\nimport { evaluateModels } from \"../tasks/evaluate-models.js\";\n\ndefineWorkflow({\n  name: \"model-regressions\",\n  metadata: { commit: process.env.GITHUB_SHA ?? \"local\" },\n  matrix: async () => ({ environment: [\"staging\", \"production\"] }),\n  tasks: ({ matrix }) => [\n    evaluateModels({\n      environment: matrix.environment,\n      baseline: \"2026-09-01\",\n    }),\n  ],\n});\n```\n\nCalling `evaluateModels(...)` binds inputs; it does not execute the Task. All\nparameter properties are required and JSON-compatible. Missing fields, optional\nparameter declarations, and incompatible bindings fail typechecking. There is no\nSDK defaults mechanism. Use setup for clients and other non-JSON resources.\n\nFor a parameterless Task, use `defineTask()({ name, run })` and bind it with no\nargument. Workflow `tasks` is always a synchronous callback returning a nonempty\nTask list. It can select different Tasks per Workflow coordinate; names must be\nunique within each coordinate. Reuse the same Task in other Workflows with new\nbindings.\n\nBoth Matrix functions run during collection. The Workflow Matrix resolves once;\nTask binding runs once per Workflow coordinate, and each bound Task discovers its\nown Matrix once. Task discovery can run concurrently across coordinates. All\nbindings and matrices are validated before executable suites are registered; a\ndiscovery failure prevents that Workflow's execution.\n\nWorkflow axes multiply Task axes. For two environments and three Task models,\nthere are six Cases. Execution follows the complete Task sequence for staging,\nthen the complete sequence for production. Each bound Task gets its own setup and\nteardown, and its Cases run concurrently under Vitest's limits. Execution failures\nnormally allow later Tasks to continue; Vitest's bail configuration controls\nstopping early.\n\nParameters are validated and snapshotted during collection and passed readonly to\nMatrix discovery, setup, run, and teardown. Task `matrix` and setup's `cases`\ncontain only Task coordinates; Workflow coordinates enter through explicit\nparameter binding. Retries share that binding's parameter snapshot and setup\nresult. Teardown runs once after successful setup.\n\nOmitting either Matrix creates one coordinate, `{}`. Axis names must be non-empty,\nenumerable strings that are not array indexes. Empty axes and duplicate values\nare errors. Vitest shows Workflow → Workflow coordinate → Task → Case, with `[1]`\nfor empty coordinates and numbered axis values otherwise.\n\n## Configure Vitest\n\nThe JSON reporter includes each Case's data at\n`assertionResults[].meta.invoker`:\n\n```ts\n// vitest.config.ts\nimport { defineConfig } from \"vitest/config\";\n\nexport default defineConfig({\n  test: {\n    maxConcurrency: 5,\n    reporters: [\"default\", \"json\", ...(process.env.GITHUB_ACTIONS === \"true\" ? [\"github-actions\" as const] : [])],\n    outputFile: {\n      json: \"./artifacts/invoker-results.json\",\n    },\n  },\n});\n```\n\nRun every Workflow or filter to one Task with ordinary Vitest commands:\n\n```sh\npnpm vitest run\npnpm vitest run regressions/workflows/model-regressions.test.ts -t evaluate-models\n```\n\nDefine additional Workflows in separate `*.test.ts` files. Vitest discovers\nthem automatically; Invoker does not scan directories or require a central\nindex.\n\nSchema 2 keeps both coordinate scopes and all bound parameters in the JSON report:\n\n```json\n{\n  \"schema\": 2,\n  \"matrix\": {\n    \"workflow\": { \"environment\": \"staging\" },\n    \"task\": { \"model\": \"model-a\", \"dataset\": \"support\" }\n  },\n  \"params\": { \"environment\": \"staging\", \"baseline\": \"2026-09-01\" },\n  \"metadata\": { \"commit\": \"abc123\" },\n  \"output\": { \"baseline\": \"2026-09-01\", \"score\": 0.92 }\n}\n```\n\n`matrix.workflow`, `matrix.task`, and `params` are always present, with `{}` for\nempty scopes. Parameters are persisted in full, including fixed configuration;\nthere is no automatic redaction. Static coordinates and parameters survive setup\nfailures, skips, and retries. `output` is present only after a successful,\nJSON-valid Task return and is cleared before every retry. Vitest's report remains authoritative for status, failures, timing, hierarchy, and\nretries.\n\n## Notify Slack\n\nInvoker's optional Slack reporter posts one `Invoker Report` parent message per\nVitest run containing every Workflow card. Each card includes aggregate results,\nWorkflow metadata, and a table with counts and durations per Workflow\ncoordinate/Task pair. Failures, retries, and skips identify both Matrix scopes.\nCollection failures produce a failed Workflow card even without collected Cases.\nFull parameters are persisted in JSON rather than printed automatically in Slack.\nA shared footer\ncontains the elapsed span from the first Case start to the final Case completion,\na localized timestamp, and the optional run link. Final failures, successful\nretry details, skipped Case reasons, and unhandled run errors are posted in the\nsame thread. The thread is limited to ten replies per run across all detail\ntypes. When details are omitted, the final reply directs readers to the run\nlogs. Delivery failures are isolated to the affected reply. Ambiguous transport\nfailures are not retried; an explicit Slack rate-limit rejection is reattempted\nonly after its required delay.\n\n```ts\nimport { slackReporter } from \"@akshatmittal/invoker/slack\";\nimport { defineConfig } from \"vitest/config\";\n\nconst runUrl =\n  process.env.GITHUB_ACTIONS === \"true\"\n    ? `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`\n    : undefined;\n\nexport default defineConfig({\n  test: {\n    reporters: [\n      \"tree\",\n      slackReporter({\n        token: process.env.SLACK_BOT_TOKEN!,\n        channel: process.env.SLACK_CHANNEL_ID!,\n        runUrl,\n      }),\n    ],\n  },\n});\n```\n\nCreate a Slack app with the `chat:write` bot scope, install it to the workspace,\nand expose its bot token and target channel ID as `SLACK_BOT_TOKEN` and\n`SLACK_CHANNEL_ID`. Invite the bot to private target channels. Slack delivery\nfailures produce a warning but do not change Vitest's exit status.\n\n## GitHub Actions artifacts\n\nCreate the output directory before Vitest and upload the report even when the\nrun fails:\n\n```yaml\n- run: mkdir -p artifacts && pnpm vitest run\n- if: always()\n  uses: actions/upload-artifact@v4\n  with:\n    name: invoker-results\n    path: artifacts/invoker-results.json\n```\n\nThe artifact provides per-run retention and can be downloaded later for custom\nqueries or reports. Invoker does not upload, index, or persist results itself.\n\n## v1 scope\n\nInvoker does not provide a CLI, directory discovery, custom runner, general\nreporter framework, configuration helper, Task-level parallelism, matrix\ninclude/exclude, or hosted result storage. Use Vitest configuration and your CI\nrunner for those concerns.\n","readmeFilename":""}