gennady 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -15
- package/ai/agents/agent-resolve-conflicts.xml +6 -0
- package/ai/agents/agent-review-verifier.xml +6 -0
- package/ai/directives/architecture/README.md +21 -0
- package/ai/directives/coding/README.md +23 -0
- package/ai/directives/coding/result-conventions.xml +55 -0
- package/ai/directives/coding/svelte5-runes.xml +248 -0
- package/ai/directives/coding/sveltekit-rules.xml +247 -0
- package/ai/directives/coding/typescript-rules.xml +589 -0
- package/ai/directives/coding/uikit-component-storybook.xml +347 -0
- package/ai/directives/coding/uikit-component-svelte.xml +344 -0
- package/ai/directives/coding/uikit-spec-drafting.xml +243 -0
- package/ai/directives/dbc-audit.directive.xml +131 -0
- package/ai/directives/dev-review.directive.xml +148 -0
- package/ai/directives/infra/README.md +11 -0
- package/ai/directives/infra/eslint-setup.xml +467 -0
- package/ai/directives/infra/git-setup.xml +261 -0
- package/ai/directives/infra/nodejs-npm-setup.xml +354 -0
- package/ai/directives/infra/storybook-setup.xml +153 -0
- package/ai/directives/knowledge.xml +206 -0
- package/ai/directives/perf-auditor/perf-auditor.directive.xml +75 -0
- package/ai/directives/perf-auditor/rules/async-latency.xml +101 -0
- package/ai/directives/perf-auditor/rules/data-structures.xml +103 -0
- package/ai/directives/perf-auditor/rules/iteration-fusion.xml +96 -0
- package/ai/directives/perf-auditor/rules/memory-gc.xml +102 -0
- package/ai/directives/sdd/README.md +49 -0
- package/ai/directives/sdd/audit.directive.xml +543 -0
- package/ai/directives/sdd/discovery.directive.xml +824 -0
- package/ai/directives/sdd/fix.directive.xml +249 -0
- package/ai/directives/sdd/module-decomposition.directive.xml +666 -0
- package/ai/directives/sdd/phase-execution-protocol.xml +339 -0
- package/ai/directives/sdd/scaffold.directive.xml +717 -0
- package/ai/directives/sdd/setup.directive.xml +213 -0
- package/ai/directives/sdd/svelte-ui-discovery.directive.xml +263 -0
- package/ai/directives/semantic-change-extractor.directive.xml +99 -0
- package/ai/directives/testing/README.md +25 -0
- package/ai/directives/testing/common.xml +234 -0
- package/ai/directives/testing/node-test.xml +288 -0
- package/ai/directives/testing/playwright-cli.xml +199 -0
- package/ai/directives/testing/playwright-e2e.xml +292 -0
- package/ai/directives/testing/storybook-usage.xml +173 -0
- package/ai/directives/testing/svelte-testing.xml +237 -0
- package/ai/directives/testing/vitest-rules.xml +326 -0
- package/ai/docs/README.md +17 -0
- package/ai/docs/ai-icl.md +538 -0
- package/ai/docs/ai-priming.md +548 -0
- package/ai/docs/ai-prompt-formats.md +406 -0
- package/ai/docs/ai-promting.md +572 -0
- package/ai/drafts/DRAFT.md +120 -0
- package/ai/drafts/agent-devgen-class-from-description.rules.xml +123 -0
- package/ai/drafts/agent-typescript-devgen.v1.xml +303 -0
- package/ai/drafts/d.md +65 -0
- package/ai/drafts/music-posts.md +105 -0
- package/ai/fw/v1/architecture/blueprint-factory.xml +77 -0
- package/ai/fw/v1/core/mental-model.xml +50 -0
- package/ai/fw/v1/discovery/intent-reactor.xml +92 -0
- package/ai/fw/v1/production/swarm-protocol.xml +122 -0
- package/ai/fw/v1/review/quality-pipeline.xml +102 -0
- package/ai/fw/v2/arch-universal.xml +73 -0
- package/ai/fw-draft/README.md +79 -0
- package/ai/fw-draft/bin/discovery.sh +116 -0
- package/ai/fw-draft/gennady.xml +58 -0
- package/ai/fw-draft/provider/claude.xml +16 -0
- package/ai/fw-draft/provider/cursor.xml +23 -0
- package/ai/fw-draft/provider/default.xml +12 -0
- package/ai/fw-draft/roles/developer.xml +18 -0
- package/ai/fw-draft/router.xml +39 -0
- package/ai/fw-draft/routes/development.xml +32 -0
- package/ai/fw-draft/routes/universal.xml +58 -0
- package/ai/fw-draft/rules/dev/base/file-structure-rules.xml +89 -0
- package/ai/fw-draft/rules/dev/typescript/contacts.xml +194 -0
- package/dist/ai/agents/agent-resolve-conflicts.xml +6 -0
- package/dist/ai/agents/agent-review-verifier.xml +6 -0
- package/dist/ai/directives/architecture/README.md +21 -0
- package/dist/ai/directives/coding/README.md +23 -0
- package/dist/ai/directives/coding/result-conventions.xml +55 -0
- package/dist/ai/directives/coding/svelte5-runes.xml +248 -0
- package/dist/ai/directives/coding/sveltekit-rules.xml +247 -0
- package/dist/ai/directives/coding/typescript-rules.xml +589 -0
- package/dist/ai/directives/coding/uikit-component-storybook.xml +347 -0
- package/dist/ai/directives/coding/uikit-component-svelte.xml +344 -0
- package/dist/ai/directives/coding/uikit-spec-drafting.xml +243 -0
- package/dist/ai/directives/dbc-audit.directive.xml +131 -0
- package/dist/ai/directives/dev-review.directive.xml +148 -0
- package/dist/ai/directives/infra/README.md +11 -0
- package/dist/ai/directives/infra/eslint-setup.xml +467 -0
- package/dist/ai/directives/infra/git-setup.xml +261 -0
- package/dist/ai/directives/infra/nodejs-npm-setup.xml +354 -0
- package/dist/ai/directives/infra/storybook-setup.xml +153 -0
- package/dist/ai/directives/knowledge.xml +206 -0
- package/dist/ai/directives/perf-auditor/perf-auditor.directive.xml +75 -0
- package/dist/ai/directives/perf-auditor/rules/async-latency.xml +101 -0
- package/dist/ai/directives/perf-auditor/rules/data-structures.xml +103 -0
- package/dist/ai/directives/perf-auditor/rules/iteration-fusion.xml +96 -0
- package/dist/ai/directives/perf-auditor/rules/memory-gc.xml +102 -0
- package/dist/ai/directives/sdd/README.md +49 -0
- package/dist/ai/directives/sdd/audit.directive.xml +543 -0
- package/dist/ai/directives/sdd/discovery.directive.xml +824 -0
- package/dist/ai/directives/sdd/fix.directive.xml +249 -0
- package/dist/ai/directives/sdd/module-decomposition.directive.xml +666 -0
- package/dist/ai/directives/sdd/phase-execution-protocol.xml +339 -0
- package/dist/ai/directives/sdd/scaffold.directive.xml +717 -0
- package/dist/ai/directives/sdd/setup.directive.xml +213 -0
- package/dist/ai/directives/sdd/svelte-ui-discovery.directive.xml +263 -0
- package/dist/ai/directives/semantic-change-extractor.directive.xml +99 -0
- package/dist/ai/directives/testing/README.md +25 -0
- package/dist/ai/directives/testing/common.xml +234 -0
- package/dist/ai/directives/testing/node-test.xml +288 -0
- package/dist/ai/directives/testing/playwright-cli.xml +199 -0
- package/dist/ai/directives/testing/playwright-e2e.xml +292 -0
- package/dist/ai/directives/testing/storybook-usage.xml +173 -0
- package/dist/ai/directives/testing/svelte-testing.xml +237 -0
- package/dist/ai/directives/testing/vitest-rules.xml +326 -0
- package/dist/ai/docs/README.md +17 -0
- package/dist/ai/docs/ai-icl.md +538 -0
- package/dist/ai/docs/ai-priming.md +548 -0
- package/dist/ai/docs/ai-prompt-formats.md +406 -0
- package/dist/ai/docs/ai-promting.md +572 -0
- package/dist/ai/drafts/DRAFT.md +120 -0
- package/dist/ai/drafts/agent-devgen-class-from-description.rules.xml +123 -0
- package/dist/ai/drafts/agent-typescript-devgen.v1.xml +303 -0
- package/dist/ai/drafts/d.md +65 -0
- package/dist/ai/drafts/music-posts.md +105 -0
- package/dist/ai/fw/v1/architecture/blueprint-factory.xml +77 -0
- package/dist/ai/fw/v1/core/mental-model.xml +50 -0
- package/dist/ai/fw/v1/discovery/intent-reactor.xml +92 -0
- package/dist/ai/fw/v1/production/swarm-protocol.xml +122 -0
- package/dist/ai/fw/v1/review/quality-pipeline.xml +102 -0
- package/dist/ai/fw/v2/arch-universal.xml +73 -0
- package/dist/ai/fw-draft/README.md +79 -0
- package/dist/ai/fw-draft/bin/discovery.sh +116 -0
- package/dist/ai/fw-draft/gennady.xml +58 -0
- package/dist/ai/fw-draft/provider/claude.xml +16 -0
- package/dist/ai/fw-draft/provider/cursor.xml +23 -0
- package/dist/ai/fw-draft/provider/default.xml +12 -0
- package/dist/ai/fw-draft/roles/developer.xml +18 -0
- package/dist/ai/fw-draft/router.xml +39 -0
- package/dist/ai/fw-draft/routes/development.xml +32 -0
- package/dist/ai/fw-draft/routes/universal.xml +58 -0
- package/dist/ai/fw-draft/rules/dev/base/file-structure-rules.xml +89 -0
- package/dist/ai/fw-draft/rules/dev/typescript/contacts.xml +194 -0
- package/dist/chunks/devtools-B-7ugZhF.js +79 -0
- package/dist/chunks/{help.cmd-CWasx25o.js → help.cmd-B_G7EWzF.js} +9 -2
- package/dist/chunks/{index-B63fYXL2.js → index-BeL1Zcbg.js} +1 -1
- package/dist/chunks/index-CVR66voe.js +26963 -0
- package/dist/chunks/index-CXuhZzS3.js +16979 -0
- package/dist/chunks/{index-B5bA2T7A.js → index-CaahtXiM.js} +33 -9
- package/dist/chunks/{index-iqg0w_pE.js → index-CiEM-8nJ.js} +35 -19
- package/dist/chunks/{index-B4m0-PAT.js → index-D1qsi0Uc.js} +57 -33
- package/dist/chunks/index-D9ceRUyB.js +443 -0
- package/dist/chunks/index-DJpVmyp2.js +176 -0
- package/dist/chunks/{index-C0andxna.js → index-DU6jD7SS.js} +1 -1
- package/dist/chunks/index-KxSZKmZn.js +540 -0
- package/dist/chunks/{index-Dqe1TdW4.js → index-UbHoePfr.js} +12 -12
- package/dist/chunks/{index-5xIgwwKx.js → index-g1LXlp77.js} +2 -2
- package/dist/chunks/index-zBnnuvLA.js +3788 -0
- package/dist/chunks/{run-review-command.logic-a_M3CkeZ.js → run-review-command.logic-DpkRoEi8.js} +2 -2
- package/dist/chunks/services-CaLOhuLV.js +2889 -0
- package/dist/chunks/shared-Bjy30TeM.js +665 -0
- package/dist/cli/cmd/_shared/prompt/io/load-agent-template.io.d.ts +3 -3
- package/dist/cli/cmd/_shared/prompt/logic/build-ai-first-knowledge-block.logic.d.ts +3 -3
- package/dist/cli/cmd/_shared/prompt/logic/build-ai-verify-placeholders.logic.d.ts +7 -4
- package/dist/cli/cmd/_shared/prompt/logic/verify-commands/resolve-verify-commands.logic.d.ts +5 -0
- package/dist/cli/cmd/_shared/update-check-worker.d.ts +1 -0
- package/dist/cli/cmd/_shared/update-check.d.ts +38 -0
- package/dist/cli/cmd/agent-mon/cmd/create-providers.d.ts +15 -0
- package/dist/cli/cmd/agent-mon/cmd/index.d.ts +1 -0
- package/dist/cli/cmd/agent-mon/cmd/run.d.ts +12 -0
- package/dist/cli/cmd/agent-mon/state/create-state-manager.d.ts +32 -0
- package/dist/cli/cmd/agent-mon/state/group-by-provider.d.ts +14 -0
- package/dist/cli/cmd/agent-mon/state/index.d.ts +5 -0
- package/dist/cli/cmd/agent-mon/state/is-waiting.d.ts +9 -0
- package/dist/cli/cmd/agent-mon/state/view-model.type.d.ts +74 -0
- package/dist/cli/cmd/agent-mon/ui/app.d.ts +16 -0
- package/dist/cli/cmd/agent-mon/ui/column-view.d.ts +14 -0
- package/dist/cli/cmd/agent-mon/ui/index.d.ts +10 -0
- package/dist/cli/cmd/agent-mon/ui/provider-column.d.ts +14 -0
- package/dist/cli/cmd/agent-mon/ui/session-card.d.ts +12 -0
- package/dist/cli/cmd/agent-mon/ui/status-badge.d.ts +10 -0
- package/dist/cli/cmd/alt-opinion/alt-opinion-parser.d.ts +18 -0
- package/dist/cli/cmd/alt-opinion/alt-opinion-runner.d.ts +32 -0
- package/dist/cli/cmd/alt-opinion/alt-opinion.cmd.d.ts +34 -0
- package/dist/cli/cmd/alt-opinion/alt-opinion.types.d.ts +88 -0
- package/dist/cli/cmd/alt-opinion/index.d.ts +1 -0
- package/dist/cli/cmd/cat/cat-url.fn.d.ts +19 -0
- package/dist/cli/cmd/lint/checks/anchor.check.d.ts +11 -0
- package/dist/cli/cmd/lint/checks/dbc-contract.check.d.ts +16 -0
- package/dist/cli/cmd/lint/checks/disables.check.d.ts +12 -0
- package/dist/cli/cmd/lint/checks/file-header.check.d.ts +10 -0
- package/dist/cli/cmd/lint/checks/language.check.d.ts +11 -0
- package/dist/cli/cmd/lint/index.d.ts +1 -0
- package/dist/cli/cmd/lint/lint.cmd.d.ts +18 -0
- package/dist/cli/cmd/lint/lint.types.d.ts +72 -0
- package/dist/cli/cmd/lint/utils/resolve-references.fn.d.ts +32 -0
- package/dist/cli/cmd/remote-console/index.d.ts +2 -0
- package/dist/cli/cmd/remote-console/remote-console.cmd.d.ts +55 -0
- package/dist/cli/cmd/resolve-conflicts/_core/io/resolve-conflicts-template-load.io.d.ts +2 -2
- package/dist/cli/cmd/resolve-conflicts/_core/logic/resolve-conflicts-command-args-parse.logic.d.ts +3 -3
- package/dist/cli/cmd/resolve-conflicts/_core/logic/resolve-conflicts-command-run.logic.d.ts +3 -3
- package/dist/cli/cmd/resolve-conflicts/_core/logic/resolve-conflicts-context-git-build.logic.d.ts +4 -4
- package/dist/cli/cmd/resolve-conflicts/_core/types/resolve-conflicts-artifact.type.d.ts +3 -1
- package/dist/cli/cmd/resolve-conflicts/_core/types/resolve-conflicts-command-args.type.d.ts +3 -1
- package/dist/cli/cmd/resolve-conflicts/_core/types/resolve-conflicts-command-result.type.d.ts +5 -1
- package/dist/cli/cmd/resolve-conflicts/_core/types/resolve-conflicts-context-git.type.d.ts +18 -2
- package/dist/cli/cmd/resolve-conflicts/_core/xml/resolve-conflicts-artifact-build.xml.d.ts +6 -6
- package/dist/cli/cmd/resolve-conflicts/_core/xml/resolve-conflicts-render.xml.d.ts +4 -4
- package/dist/cli/cmd/review/_core/io/load-review-verify-template.io.d.ts +2 -2
- package/dist/cli/cmd/review/_core/logic/build-review-context-git.logic.d.ts +3 -3
- package/dist/cli/cmd/review/_core/logic/build-review-context-vcs.logic.d.ts +2 -2
- package/dist/cli/cmd/review/_core/logic/load-review-context-mr.logic.d.ts +5 -5
- package/dist/cli/cmd/review/_core/logic/parse-review-command-args.logic.d.ts +3 -3
- package/dist/cli/cmd/review/_core/logic/resolve-review-intent.logic.d.ts +3 -3
- package/dist/cli/cmd/review/_core/logic/run-review-command.logic.d.ts +3 -3
- package/dist/cli/cmd/review/_core/types/review-artifact.type.d.ts +4 -1
- package/dist/cli/cmd/review/_core/types/review-command-args.type.d.ts +7 -1
- package/dist/cli/cmd/review/_core/types/review-command-mode.type.d.ts +1 -1
- package/dist/cli/cmd/review/_core/types/review-command-options.type.d.ts +3 -1
- package/dist/cli/cmd/review/_core/types/review-command-result.type.d.ts +5 -1
- package/dist/cli/cmd/review/_core/types/review-context-git.type.d.ts +3 -1
- package/dist/cli/cmd/review/_core/types/review-context-mr.type.d.ts +22 -3
- package/dist/cli/cmd/review/_core/types/review-context-vcs.type.d.ts +4 -1
- package/dist/cli/cmd/review/_core/types/review-intent.type.d.ts +1 -1
- package/dist/cli/cmd/review/_core/xml/build-review-artifact.xml.d.ts +10 -10
- package/dist/cli/cmd/review/_core/xml/render-review-issues.xml.d.ts +3 -3
- package/dist/cli/cmd/review/_core/xml/render-review-verify.xml.d.ts +4 -4
- package/dist/cli/cmd/sync/index.d.ts +1 -0
- package/dist/cli/cmd/sync/sync-core.d.ts +65 -0
- package/dist/cli/cmd/sync/sync-formatter.d.ts +12 -0
- package/dist/cli/cmd/sync/sync.cmd.d.ts +33 -0
- package/dist/cli/cmd/sync/sync.types.d.ts +50 -0
- package/dist/cli/cmd/vcs-reply/vcs-reply.cmd.d.ts +4 -4
- package/dist/cli/utils/ai-legacy/ai-legacy-agent.d.ts +12 -11
- package/dist/cli/utils/ai-legacy/ai-legacy-core.d.ts +33 -17
- package/dist/cli/utils/ai-legacy/ai-legacy-model.d.ts +42 -23
- package/dist/cli/utils/cat-gen/cat-gen.d.ts +19 -8
- package/dist/cli/utils/commit-gen/commit-gen.d.ts +39 -15
- package/dist/cli/utils/prompts/index.d.ts +6 -6
- package/dist/cli/utils/review-gen/review-gen.d.ts +33 -9
- package/dist/gennady.js +30 -10
- package/dist/index.d.ts +1 -0
- package/dist/index.js +44 -23
- package/dist/services/agent-mon/diff/diff.d.ts +9 -0
- package/dist/services/agent-mon/diff/index.d.ts +1 -0
- package/dist/services/agent-mon/index.d.ts +10 -0
- package/dist/services/agent-mon/model/agent-provider.type.d.ts +20 -0
- package/dist/services/agent-mon/model/agent-session.type.d.ts +49 -0
- package/dist/services/agent-mon/model/errors.d.ts +20 -0
- package/dist/services/agent-mon/model/index.d.ts +7 -0
- package/dist/services/agent-mon/model/observe-opts.type.d.ts +7 -0
- package/dist/services/agent-mon/model/scan-opts.type.d.ts +7 -0
- package/dist/services/agent-mon/model/session-changes.type.d.ts +10 -0
- package/dist/services/agent-mon/monitor/agent-monitor.d.ts +44 -0
- package/dist/services/agent-mon/monitor/create-monitor.d.ts +6 -0
- package/dist/services/agent-mon/monitor/index.d.ts +2 -0
- package/dist/services/agent-mon/observe/index.d.ts +1 -0
- package/dist/services/agent-mon/observe/observe.d.ts +12 -0
- package/dist/services/agent-mon/providers/claude/claude-provider.d.ts +50 -0
- package/dist/services/agent-mon/providers/claude/index.d.ts +5 -0
- package/dist/services/agent-mon/providers/claude/ps.d.ts +36 -0
- package/dist/services/agent-mon/providers/claude/session-json.d.ts +37 -0
- package/dist/services/agent-mon/providers/opencode/db.d.ts +41 -0
- package/dist/services/agent-mon/providers/opencode/index.d.ts +3 -0
- package/dist/services/agent-mon/providers/opencode/model-parser.d.ts +6 -0
- package/dist/services/agent-mon/providers/opencode/opencode-provider.d.ts +45 -0
- package/dist/services/ai-client/providers/ai-model.type.d.ts +14 -0
- package/dist/services/ai-client/providers/open-router/open-router-model.type.d.ts +1 -4
- package/dist/services/ai-client/providers/open-router/open-router-provider.d.ts +17 -0
- package/dist/services/ai-client/providers/openai-like-provider.d.ts +1 -0
- package/dist/services/data-ore/telegram/telegram-data-ore.d.ts +17 -0
- package/dist/services/data-ore/telegram/telegram-data-ore.types.d.ts +4 -0
- package/dist/services/data-ore/telegram/telegram-demo-music-helper.d.ts +1 -0
- package/dist/services/data-ore/telegram/telegram-demo.d.ts +1 -0
- package/dist/services/dbc/linter/dbc-ast-adapter.types.d.ts +82 -0
- package/dist/services/dbc/linter/dbc-linter.types.d.ts +95 -0
- package/dist/services/dbc/linter/implementations/ts/dbc-ts-ast-adapter.d.ts +183 -0
- package/dist/services/dbc/linter/implementations/ts/dbc-ts-linter.d.ts +152 -0
- package/dist/services/dbc/parser/dbc-parser.types.d.ts +16 -16
- package/dist/services/dbc/parser/implementations/jsdoc/dbc-jsdoc-parser.d.ts +14 -4
- package/dist/services/logger/logger.d.ts +26 -6
- package/dist/services/remote-console/client/remote-console-client-serializer.d.ts +7 -0
- package/dist/services/remote-console/client/remote-console-client.d.ts +20 -0
- package/dist/services/remote-console/client/remote-console-client.types.d.ts +88 -0
- package/dist/services/remote-console/remote-console.d.ts +6 -0
- package/dist/services/remote-console/server/remote-console-server.d.ts +10 -0
- package/dist/services/remote-console/server/remote-console-server.types.d.ts +44 -0
- package/dist/services/remote-console/server/remote-console-stdout-writer.d.ts +26 -0
- package/dist/services/vcs-client/abstract/vcs-client-merge-discussions.d.ts +22 -14
- package/dist/services/vcs-client/abstract/vcs-client-merge-requests.d.ts +31 -14
- package/dist/services/vcs-client/abstract/vcs-client-repository-files.d.ts +16 -0
- package/dist/services/vcs-client/abstract/vcs-client.d.ts +9 -5
- package/dist/services/vcs-client/entities/vcs-file-content.type.d.ts +24 -0
- package/dist/services/vcs-client/entities/vcs-merge-request-changes.type.d.ts +32 -0
- package/dist/services/vcs-client/entities/vcs-url.type.d.ts +14 -0
- package/dist/services/vcs-client/entities/vcs-user.type.d.ts +3 -0
- package/dist/services/vcs-client/github/vcs-github-client.d.ts +31 -0
- package/dist/services/vcs-client/github/vcs-github-merge-requests.d.ts +40 -0
- package/dist/services/vcs-client/github/vcs-github-repository-files.d.ts +27 -0
- package/dist/services/vcs-client/gitlab/vcs-gitlab-client.d.ts +13 -4
- package/dist/services/vcs-client/gitlab/vcs-gitlab-merge-discussions.d.ts +16 -5
- package/dist/services/vcs-client/gitlab/vcs-gitlab-merge-requests.d.ts +24 -5
- package/dist/services/vcs-client/gitlab/vcs-gitlab-repository-files.d.ts +27 -0
- package/dist/services/vcs-client/parse-vcs-url.d.ts +3 -0
- package/dist/shared/backend/git/git-core.d.ts +24 -22
- package/dist/shared/backend/git/git-diff.d.ts +16 -5
- package/dist/shared/backend/rc/rc-config.d.ts +27 -15
- package/dist/shared/common/exec.d.ts +4 -6
- package/dist/shared/common/files.d.ts +1 -3
- package/dist/shared/common/language.d.ts +3 -6
- package/dist/shared/common/parse-args.d.ts +1 -4
- package/dist/shared/common/style.d.ts +3 -3
- package/dist/shared/common/think.d.ts +1 -5
- package/dist/shared/common/tokens.d.ts +1 -5
- package/dist/shared/common/unguard.d.ts +4 -8
- package/dist/shared/common/xml.d.ts +16 -12
- package/package.json +20 -4
- package/dist/.ai/agents/agent-review-verifier.xml +0 -181
- package/dist/chunks/index-CNbmXK8M.js +0 -3548
- package/dist/chunks/services-Sb7TwLxt.js +0 -122
- package/dist/chunks/shared-BgLzFWMH.js +0 -577
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
<TestingCommon keywords="testing, ai_first_tests, contract_testing, case_flow, phase_anchors, unified_context, factory, mocks, bdd_mapping, snapshots, isolation" type="testing-rules" ver="1.0">
|
|
2
|
+
<Mission>
|
|
3
|
+
Shared testing core for every runner-specific directive (`testing/node-test`, `testing/vitest-rules`, future Jest/Pytest/Go directives). Defines: what a test must protect, how the case body is structured for the next agent to read, how dependencies are isolated, how BDD scenarios in a ticket map to `it` cases.
|
|
4
|
+
|
|
5
|
+
Runner-specific directives reference this file under `Inherited_Baseline` and only state the differences (assertion API, mocking surface, lifecycle vocabulary, snapshot location).
|
|
6
|
+
|
|
7
|
+
**A test is executable learning context.** Title, structural anchors, intent comments must reveal what is under protection, what must not regress, where the scenario is fragile. Tests are CODE — they obey the same machine-first annotation language as production code (`coding/typescript-rules`). The repo reads as one system.
|
|
8
|
+
</Mission>
|
|
9
|
+
|
|
10
|
+
<Belief_State>
|
|
11
|
+
|
|
12
|
+
<!-- ── Contract boundary ──────────────────────────────────────────────── -->
|
|
13
|
+
|
|
14
|
+
<Axiom id="AX_CONTRACT_OVER_IMPLEMENTATION">
|
|
15
|
+
Test the public contract; never inspect protected/private state of the SUT. A refactor that preserves behavior must not break the test suite. Applies uniformly to:
|
|
16
|
+
- state-returning methods → assert returned value;
|
|
17
|
+
- emitters / subscriptions → assert emitted event payload via a public listener; do NOT inspect listener arrays or registration mechanics;
|
|
18
|
+
- cancellation APIs → assert observable signal (`aborted`, `reason`, downstream reaction); do NOT inspect controller wiring;
|
|
19
|
+
- lifecycle hooks → assert the side effect at the public boundary, not internal counters.
|
|
20
|
+
|
|
21
|
+
Tests bound to internals become an obligation for every refactor and lose the ability to catch real regressions.
|
|
22
|
+
</Axiom>
|
|
23
|
+
|
|
24
|
+
<Axiom id="AX_ISOLATION_THROUGH_PUBLIC_BOUNDARY">
|
|
25
|
+
Isolate the SUT by stubbing its external dependencies and exercising it through its public surface. Determinism comes from controlling inputs at the boundary, not from reaching past the boundary to set internal state.
|
|
26
|
+
</Axiom>
|
|
27
|
+
|
|
28
|
+
<!-- ── Case flow + anchors ────────────────────────────────────────────── -->
|
|
29
|
+
|
|
30
|
+
<Axiom id="AX_CASE_FLOW">
|
|
31
|
+
Canonical case pattern: `SETUP → TRIGGER → OBSERVE → ASSERT` (`CLEANUP` when needed). Keep only the phases that add clarity. `AAA` is a useful short form — not a law. Forced empty phases are noise; merging obvious phases (`TRIGGER_AND_ASSERT` around `assert.rejects` / `rejects.toThrow`) is allowed and preferred over a fake split.
|
|
32
|
+
|
|
33
|
+
TRIGGER and ASSERT are mandatory in every non-trivial case (trigger crosses the contract boundary; assert verifies it).
|
|
34
|
+
</Axiom>
|
|
35
|
+
|
|
36
|
+
<Axiom id="AX_PHASE_ANCHORS">
|
|
37
|
+
**Wrap each present phase that carries ≥2 statements OR encodes local policy** (retry, fallback, aggregation, fixture build) in paired anchors:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
// #region START_[CASE]_[PHASE]_[INTENT]
|
|
41
|
+
...phase body...
|
|
42
|
+
// #endregion END_[CASE]_[PHASE]_[INTENT]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
PHASE ∈ {`SETUP`, `TRIGGER`, `OBSERVE`, `ASSERT`, `CLEANUP`}.
|
|
46
|
+
|
|
47
|
+
**Skip anchors when the phase is a single statement with no hidden policy.** A one-line `await assert.rejects(...)` covering both TRIGGER and ASSERT does NOT need an anchor — title + the call already expose intent. Anchors around trivial one-liners are ritual noise that erodes the signal of anchors on truly non-trivial phases.
|
|
48
|
+
|
|
49
|
+
Anchors are machine grammar for refactoring: they let the next agent cut and replace phases by pairs without an AST.
|
|
50
|
+
</Axiom>
|
|
51
|
+
|
|
52
|
+
<Axiom id="AX_TEST_ANCHOR_NAMING">
|
|
53
|
+
Anchor names describe intent, not raw syntax. `START_FETCH_ITEM_OBSERVE_RETRY_SUMMARY` — yes; `START_FETCH_ITEM_ASSERT_2` — no. Uppercase, high-signal tokens. CASE = short scenario id; INTENT = what this phase is about.
|
|
54
|
+
</Axiom>
|
|
55
|
+
|
|
56
|
+
<!-- ── Brief ──────────────────────────────────────────────────────────── -->
|
|
57
|
+
|
|
58
|
+
<Axiom id="AX_IT_OPENING_BRIEF">
|
|
59
|
+
Open the `it` body with a 1–3 line brief **when the title alone does not convey invariant / failure mode / non-goal**. Brief never duplicates the title — it adds what the title cannot.
|
|
60
|
+
|
|
61
|
+
Closed vocabulary (machine-first payload): `purpose`, `consumer`, `invariant`, `side effect`, `failure mode`, `non-goal`, `contract`, `observation focus`.
|
|
62
|
+
|
|
63
|
+
Skip the brief on cases where title + direct input + a single assertion already expose the scenario. A brief on a self-evident case is noise that weakens the signal of briefs on hard cases.
|
|
64
|
+
</Axiom>
|
|
65
|
+
|
|
66
|
+
<!-- ── Assertion shape ────────────────────────────────────────────────── -->
|
|
67
|
+
|
|
68
|
+
<Axiom id="AX_DEFAULT_DIRECT_ASSERTION_PROTOCOL">
|
|
69
|
+
Start with the simplest direct assertion on the real contract surface. Introduce an OBSERVE phase with an aggregated `actual` object only when a single rich diff genuinely beats several isolated assertions (composite contract: returned value + emitted log + side-effect summary). Do NOT fragment one coherent shape into many tiny assertions; do NOT force aggregation when orthogonal observations read more clearly standalone. The criterion is **diagnostic clarity of the failure report**.
|
|
70
|
+
|
|
71
|
+
Concrete assertion API (`strictEqual` / `toBe`, `deepStrictEqual` / `toEqual`, `throws` / `toThrow`, etc.) lives in the runner-specific directive.
|
|
72
|
+
</Axiom>
|
|
73
|
+
|
|
74
|
+
<Axiom id="AX_PARTIAL_STRING_VIA_MATCH">
|
|
75
|
+
Partial string / error-message checks go through regex match (`assert.match` / `toMatch`), never through boolean fold (`includes`, `toContain`). Match shows the real value in the failure output; boolean fold loses context.
|
|
76
|
+
</Axiom>
|
|
77
|
+
|
|
78
|
+
<Axiom id="AX_RESULT_UNWRAP_ON_SUCCESS_PATHS">
|
|
79
|
+
If the SUT returns `Result<T>`, use `unwrap()` / `unwrapSync()` on success paths to collapse «check success + extract value» into one call. The assert then focuses solely on the business shape.
|
|
80
|
+
</Axiom>
|
|
81
|
+
|
|
82
|
+
<Axiom id="AX_FOCUSED_ERROR_ASSERTIONS">
|
|
83
|
+
Error cases use a focused set: domain error class via `instanceof` AND a stable message fragment via regex match. Do NOT strict-equal a dynamic error message (correlation ids, timestamps) — flaky failures with no diagnostic signal.
|
|
84
|
+
</Axiom>
|
|
85
|
+
|
|
86
|
+
<!-- ── Unified context + factory ──────────────────────────────────────── -->
|
|
87
|
+
|
|
88
|
+
<Axiom id="AX_ONE_UNIFIED_CONTEXT_PER_FILE">
|
|
89
|
+
**Each test file defines exactly ONE context shape** — a single `XxxContext` type and a single factory `createXxxContext(overrides?)` (or, when teardown required, a single lifecycle context object built in `beforeEach` and disposed in `afterEach`).
|
|
90
|
+
|
|
91
|
+
Every case in the file consumes this same context. New requirements extend the existing shape and factory through optional fields and overrides; they do NOT spawn parallel `createOrderContextHappy` / `createOrderContextFailing` factories or sibling `let`s in the describe.
|
|
92
|
+
|
|
93
|
+
A nested `describe` MAY introduce its own narrower context only when its sub-group has lifecycle needs that cannot be expressed as overrides on the parent; the exception must be obvious from the diff.
|
|
94
|
+
|
|
95
|
+
Why: test files stay small and predictable — one shape applies to every case, new tests almost always add an override, marginal cost per case stays flat.
|
|
96
|
+
</Axiom>
|
|
97
|
+
|
|
98
|
+
<Axiom id="AX_PREFER_FACTORY_OVER_HOOKS">
|
|
99
|
+
Default preparation = the file's single factory called inside the case. Each case constructs the exact context it needs by `createXxxContext({ ... overrides })` at the top of the case, destructures only the fields it uses, and proceeds. Per-case dependencies become visible at the call site; no scrolling to a hook; no shared mutable `let` populated in `beforeEach`.
|
|
100
|
+
|
|
101
|
+
`beforeEach` is reserved for setup that REQUIRES teardown (see `AX_HOOKS_ONLY_FOR_LIFECYCLE`).
|
|
102
|
+
</Axiom>
|
|
103
|
+
|
|
104
|
+
<Axiom id="AX_HOOKS_ONLY_FOR_LIFECYCLE">
|
|
105
|
+
`beforeEach` / `afterEach` are reserved for context that REQUIRES teardown — open resources (timers, sockets, file handles, DB connections), global stubs (env, globals), module mocks, fake timers, fixture filesystems, in-process servers.
|
|
106
|
+
|
|
107
|
+
A justified hook MUST follow the **single-context shape**: hook builds ONE context object and `afterEach` disposes it. Cases consume properties off that single object — no pile of `let sut, let saveOrder, let publish` in describe scope.
|
|
108
|
+
</Axiom>
|
|
109
|
+
|
|
110
|
+
<Axiom id="AX_NO_DESCRIBE_LET_PILEUP">
|
|
111
|
+
`describe`-scope MUST NOT host mutable `let` variables populated by hooks. Static fixtures (frozen constants, fixture paths, factory definitions, type aliases) are allowed. Anything mutable per-case lives either inside the case (factory result) or inside the SINGLE lifecycle context object exposed by the hook. Mutable describe-scope state is the structural source of inter-test leakage.
|
|
112
|
+
</Axiom>
|
|
113
|
+
|
|
114
|
+
<!-- ── Mocking discipline ─────────────────────────────────────────────── -->
|
|
115
|
+
|
|
116
|
+
<Axiom id="AX_MOCK_AS_LAST_RESORT">
|
|
117
|
+
Prefer real modules with dependency injection over module-level mocks. A SUT that accepts its collaborators as constructor or function arguments is tested by passing test doubles directly through those seams — no module-level mocking needed.
|
|
118
|
+
|
|
119
|
+
Module-level mocking is allowed ONLY when ALL of:
|
|
120
|
+
1. Dependency is genuinely external (network, FS, process, OS time, randomness, third-party SDKs touching the network).
|
|
121
|
+
2. No available injection seam in the SUT (refactoring to add one is out of scope).
|
|
122
|
+
3. Case carries an explicit justification comment naming the dependency and why injection is unavailable.
|
|
123
|
+
|
|
124
|
+
Mocks of pure in-process neighboring modules are forbidden by default. Every mock freezes an assumption about another module's behavior; injection keeps the seam observable in the type system.
|
|
125
|
+
</Axiom>
|
|
126
|
+
|
|
127
|
+
<Axiom id="AX_NO_FALSIFICATION_VIA_MOCKS">
|
|
128
|
+
A test MUST NOT mock the very module that contains the SUT, its direct collaborator under test, or the contract method being verified. Mocking the SUT or its co-tested collaborator turns the test into a tautology — it asserts the mock instead of production code. Non-negotiable; most common form of fabricated green tests in agent-written suites.
|
|
129
|
+
</Axiom>
|
|
130
|
+
|
|
131
|
+
<Axiom id="AX_FIXTURE_IO_POLICY">
|
|
132
|
+
Reading fixture files is allowed. Writing files inside unit tests is NOT. Writes from a test make the runtime stateful, break parallelism, and leave artifacts in FS that mutate between runs.
|
|
133
|
+
</Axiom>
|
|
134
|
+
|
|
135
|
+
<!-- ── Snapshots ──────────────────────────────────────────────────────── -->
|
|
136
|
+
|
|
137
|
+
<Axiom id="AX_SNAPSHOT_USAGE_GATE">
|
|
138
|
+
Snapshots ONLY for large stable serializable outputs (generated text, HTML, JSON documents, codegen). Do NOT replace scalar or small-object equality with a snapshot — failure shows a diff of snapshot blobs instead of the actual mismatch, and manual review of every scalar change is ceremony with no payoff.
|
|
139
|
+
</Axiom>
|
|
140
|
+
|
|
141
|
+
<Axiom id="AX_SNAPSHOT_OPERATOR_CONFIRM">
|
|
142
|
+
**Execution-agent MUST NOT update existing snapshots autonomously.** Forbidden: running snapshot-update flags (`vitest -u`, `--update`); editing snapshot literals to make a test pass; deleting snapshot files.
|
|
143
|
+
|
|
144
|
+
Allowed: writing the FIRST snapshot for a NEW test (operator reviews in PR/diff). For any change to an existing snapshot the agent stops, presents old vs new content, and waits for explicit operator confirmation. Auto-update is the most direct path an agent has to fabricate a green test.
|
|
145
|
+
</Axiom>
|
|
146
|
+
|
|
147
|
+
<!-- ── File shape + scenario hygiene ──────────────────────────────────── -->
|
|
148
|
+
|
|
149
|
+
<Axiom id="AX_FILE_NAME_AND_DEFAULT_SHAPE">
|
|
150
|
+
File naming: `[subject].test.ts`. Default shape: `describe(subject)` → optional shared hooks → nested `describe(method)` → `it(case)`. A `Test Graph` header comment (map of cases) is RECOMMENDED for multi-method or non-trivial files, OPTIONAL for tiny focused ones.
|
|
151
|
+
</Axiom>
|
|
152
|
+
|
|
153
|
+
<Axiom id="AX_TEST_FILE_SIZE_BUDGET">
|
|
154
|
+
**Soft target: ≤300 code-LOC per test file. Hard ceiling: 500.** Split axes in priority order: (1) by SUT method — `[subject].[method].test.ts`, sharing factory via a sibling `[subject].test-context.ts`; (2) by scenario family — `[subject].error-paths.test.ts` separate from happy-paths. After a split each file STILL holds exactly one context shape (`AX_ONE_UNIFIED_CONTEXT_PER_FILE`).
|
|
155
|
+
|
|
156
|
+
Why: instruction-following degrades with file size. Past 500 LOC the agent loses structure between read/edit cycles → fabricated green tests slip in.
|
|
157
|
+
</Axiom>
|
|
158
|
+
|
|
159
|
+
<Axiom id="AX_ONE_TEST_ONE_SCENARIO">
|
|
160
|
+
One `it` covers one scenario. Duplicate cases that prove the same behavior create false confidence in coverage and double maintenance cost when the contract changes.
|
|
161
|
+
</Axiom>
|
|
162
|
+
|
|
163
|
+
<Axiom id="AX_TESTS_NO_HUMAN_NARRATION">
|
|
164
|
+
Never write step-by-step narration like `Step 1`, `Step 2` inside a case. Phase anchors already make the steps machine-visible. Comments only where they increase recoverable intent; do not duplicate the title, assertion text, or obvious setup facts.
|
|
165
|
+
</Axiom>
|
|
166
|
+
|
|
167
|
+
<Axiom id="AX_ENGLISH_ONLY_NAMES_AND_COMMENTS">
|
|
168
|
+
Test titles, comments, anchors, helper variable names — English only. Mixed languages break grep and comprehension.
|
|
169
|
+
</Axiom>
|
|
170
|
+
|
|
171
|
+
<Axiom id="AX_NO_FOCUSED_OR_SKIPPED_TESTS_ON_MERGE">
|
|
172
|
+
`it.only` / `describe.only` / `it.skip` / `describe.skip` / `it.todo` MUST NOT be present in committed code unless paired with an inline reference to a tracked deferred-ownership task. A focused `.only` silently disables the rest of the suite; a stray `.skip` rots into a coverage hole.
|
|
173
|
+
</Axiom>
|
|
174
|
+
|
|
175
|
+
<!-- ── Coverage + finalization ────────────────────────────────────────── -->
|
|
176
|
+
|
|
177
|
+
<Axiom id="AX_COVERAGE_BY_CONTRACT_NOT_BY_LINE">
|
|
178
|
+
Measure CONTRACT coverage, not line coverage. Mandatory minimum per public method/function:
|
|
179
|
+
1. ≥1 happy-path scenario.
|
|
180
|
+
2. ≥1 boundary scenario (boundary value, edge of input domain) when contract has boundary semantics.
|
|
181
|
+
3. ≥1 failure-path scenario per declared `@throws` / rejected promise.
|
|
182
|
+
|
|
183
|
+
Ignored: thin pass-through wrappers without hidden policy, type-only constructs, dead branches excluded by `AX_MINIMAL_ERROR_SURFACE` in coding-rules.
|
|
184
|
+
</Axiom>
|
|
185
|
+
|
|
186
|
+
<Axiom id="AX_COVERAGE_REPORT_BLOCKER_EXPLICIT">
|
|
187
|
+
Type-check or coverage cannot run because of an environment / project blocker → declare the blocker EXPLICITLY; do not simulate success. A silent skip turns the completion gate into fiction.
|
|
188
|
+
</Axiom>
|
|
189
|
+
|
|
190
|
+
<Axiom id="AX_VERIFY_AND_FINALIZE">
|
|
191
|
+
Finalizing a test = re-checking isolation, naming, anchor pairing, snapshot placement, and the diagnostic quality of failures, then running type-check + tests with coverage. Blocker → declared EXPLICITLY.
|
|
192
|
+
</Axiom>
|
|
193
|
+
|
|
194
|
+
</Belief_State>
|
|
195
|
+
|
|
196
|
+
<Definitions>
|
|
197
|
+
<Definition id="DEF_NON_TRIVIAL_CASE">
|
|
198
|
+
A case is non-trivial if any of: async behavior with setup; retries / fallbacks / errors; emitted events or external side effects; more than one meaningful observation; nested control flow that another agent could misread at a glance. Non-trivial cases are candidates for opening brief and phase anchors per the thresholds in `AX_PHASE_ANCHORS` and `AX_IT_OPENING_BRIEF`.
|
|
199
|
+
</Definition>
|
|
200
|
+
<Definition id="DEF_PHASE_ANCHOR_FORMAT">
|
|
201
|
+
`// #region START_[CASE]_[PHASE]_[INTENT]` and paired `// #endregion END_[CASE]_[PHASE]_[INTENT]`. PHASE ∈ {`SETUP`, `TRIGGER`, `OBSERVE`, `ASSERT`, `CLEANUP`}. CASE = uppercase short scenario id; INTENT = uppercase intent token.
|
|
202
|
+
</Definition>
|
|
203
|
+
<Definition id="DEF_TEST_PAYLOAD_VOCAB">
|
|
204
|
+
Closed vocabulary for intent comments inside tests: `purpose`, `consumer`, `invariant`, `side effect`, `failure mode`, `non-goal`, `contract`, `observation focus`. Same base as production code (`coding/typescript-rules`), plus test-specific `contract` and `observation focus`.
|
|
205
|
+
</Definition>
|
|
206
|
+
<Definition id="DEF_UNIFIED_CONTEXT">
|
|
207
|
+
The single per-file `XxxContext` type plus its single factory `createXxxContext(overrides?)` (or the single `beforeEach`/`afterEach`-managed lifecycle object). Every case in the file uses this one shape. New scenarios extend it via optional fields and overrides rather than introducing parallel structures.
|
|
208
|
+
</Definition>
|
|
209
|
+
<Definition id="DEF_INJECTION_SEAM">
|
|
210
|
+
A constructor parameter, function argument, or factory option of the SUT through which a collaborator can be supplied from outside. Tests use injection seams to pass test doubles without resorting to module-level mocks.
|
|
211
|
+
</Definition>
|
|
212
|
+
</Definitions>
|
|
213
|
+
|
|
214
|
+
<BDD_Mapping_Hint>
|
|
215
|
+
Public contract between `task-scaffolding` and any execution-agent writing tests under a runner-specific directive that inherits this file.
|
|
216
|
+
|
|
217
|
+
- **`BDD_CANONICAL_CASE_NAME_BINDING`** — canonical case name from ticket MUST be used verbatim as the `it(...)` title. Execution-agent changes a name → MUST update `Test Scenario Coverage` in the ticket BEFORE closing.
|
|
218
|
+
- **`BDD_SCENARIO_TO_IT`** — one BDD scenario → one `it(...)` case (1:1). Don't merge scenarios; don't split a scenario without explicit derivation.
|
|
219
|
+
- **`BDD_FEATURE_TO_DESCRIBE`** — `Feature: [Component Behavior]` → outer `describe(...)` (or semantically equivalent SUT name). Each `Scenario:` → separate `it(...)`.
|
|
220
|
+
- **`BDD_GIVEN_WHEN_THEN_TO_CASE_FLOW`** — `Given` → SETUP phase (or `beforeEach` if shared and lifecycle-bound). `When` → TRIGGER. `Then` → ASSERT. `And` after `Then` → additional assertion in ASSERT or a separate OBSERVE if aggregation justified.
|
|
221
|
+
- **`BDD_ERROR_SCENARIO_BINDING`** — error / rejection scenario → assert-throws / assert-rejects (runner-specific API). Assert message via regex match (`AX_PARTIAL_STRING_VIA_MATCH`). Domain error class → assert both `instanceof` AND message pattern.
|
|
222
|
+
- **`BDD_VERIFICATION_SURFACE_GUARDRAIL`** — `Test Scenario Coverage` carries a `Runtime Fidelity` marker. Tests under this directive cover ONLY `contract-only` and `simulation-backed`. `runtime-hook-required` and `e2e-required` are out of scope; record deferred coverage owner EXPLICITLY in Execution Log.
|
|
223
|
+
- **`BDD_DEFERRED_OWNERSHIP_HONESTY`** — BDD scenario cannot be materialized at this runner level → silent skipping forbidden; add deferred-ownership reference in `Test Scenario Coverage`.
|
|
224
|
+
</BDD_Mapping_Hint>
|
|
225
|
+
|
|
226
|
+
<Workflow_Outline>
|
|
227
|
+
<Step id="WF_1_DERIVE_SCENARIOS">Read contract + ticket's `Test Scenario Coverage`. Derive happy-path, boundary, failure scenarios. BDD scenarios are normative source of case names.</Step>
|
|
228
|
+
<Step id="WF_2_DEFINE_UNIFIED_CONTEXT">Define ONE `XxxContext` type and ONE factory at top of file. Teardown required → ONE lifecycle context object in `beforeEach`/`afterEach`. Choose ONE preparation path for the whole file.</Step>
|
|
229
|
+
<Step id="WF_3_BUDGET_CHECK">Estimate file size for planned cases. Projection >300 LOC → plan a split now (by method or scenario family); unified context survives the split.</Step>
|
|
230
|
+
<Step id="WF_4_IMPLEMENT_CASES">For each `it`: decide if title alone conveys intent (skip brief) or not (add brief from closed vocab). Wrap phases ≥2 statements or carrying policy in anchors; skip anchors on one-liners. Pass stubs through injection seams.</Step>
|
|
231
|
+
<Step id="WF_5_CHOOSE_DIAGNOSTIC_ASSERTIONS">Simplest direct assertion first. Aggregate into OBSERVE only when one rich diff beats several isolated assertions.</Step>
|
|
232
|
+
<Step id="WF_6_VERIFY_AND_FINALIZE">Re-check unified context, anchor pairing, snapshot placement, no ungated mocks. Run type-check + tests with coverage. Blocker → declared EXPLICITLY.</Step>
|
|
233
|
+
</Workflow_Outline>
|
|
234
|
+
</TestingCommon>
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
<NodeTestRules keywords="node-test, runner, assertions, native_mock_fn, http_mock_agent, snapshot_files" type="testing-rules" ver="3.0">
|
|
2
|
+
<Mission>
|
|
3
|
+
Node.js built-in `node:test` runner (`node --test`) specifics on top of [`testing/common`](./common.xml). Only the deltas are stated here: assertion API mapping, native `mock.fn()` usage, HTTP mock-agent harness, snapshot file location.
|
|
4
|
+
|
|
5
|
+
Read `testing/common.xml` first. Everything about contract boundary, case flow, phase anchors, unified context, factory-over-hooks, BDD mapping, snapshot operator-confirm, file budget — lives there.
|
|
6
|
+
</Mission>
|
|
7
|
+
|
|
8
|
+
<Inherited_Baseline>
|
|
9
|
+
All axioms in [`testing/common`](./common.xml) `Belief_State` apply here UNCHANGED — reader-agent treats them as binding under this directive. `BDD_Mapping_Hint`, `Definitions`, `Workflow_Outline` likewise inherited. This file only adds node:test-specific overrides and patterns.
|
|
10
|
+
</Inherited_Baseline>
|
|
11
|
+
|
|
12
|
+
<Belief_State>
|
|
13
|
+
|
|
14
|
+
<Axiom id="AX_ASSERT_API_CHOICE">
|
|
15
|
+
Pick the simplest direct match by shape (node:test assert API):
|
|
16
|
+
- `assert.strictEqual(actual, expected)` — scalars and reference identity.
|
|
17
|
+
- `assert.deepStrictEqual(actual, expected)` — structured results (deep, type-strict).
|
|
18
|
+
- `assert.throws(fn, matcher)` / `await assert.rejects(promise, matcher)` — thrown failures.
|
|
19
|
+
- `assert.match(str, regex)` — partial string / error-message checks (per inherited `AX_PARTIAL_STRING_VIA_MATCH`).
|
|
20
|
+
- `t.assert.snapshot(value)` / `t.assert.fileSnapshot(value, path)` — ONLY for large stable serializable outputs (per inherited `AX_SNAPSHOT_USAGE_GATE`).
|
|
21
|
+
|
|
22
|
+
Each function has a unique diagnostic signature. A mismatched choice (snapshot for a scalar, `includes` on a message) degrades the failure report.
|
|
23
|
+
|
|
24
|
+
Forbidden idioms: `assert.ok(x.includes(y))` on error messages → use `assert.match`. Manual `let threw = false; try {...} catch {...} assert.ok(threw)` → use `assert.throws` / `assert.rejects`.
|
|
25
|
+
</Axiom>
|
|
26
|
+
|
|
27
|
+
<Axiom id="AX_PREFER_NATIVE_MOCK_FN">
|
|
28
|
+
Use `mock.fn()` from `node:test` for tracking calls and stubbed behavior on injected collaborators. Native API requires no extra dependencies and integrates with the runner (cleanup, assertion helpers).
|
|
29
|
+
|
|
30
|
+
Common surface:
|
|
31
|
+
- `const fn = mock.fn(async () => 'ok')` — create stub with implementation.
|
|
32
|
+
- `fn.mock.callCount()` / `fn.mock.calls[i].arguments` — interaction assertions.
|
|
33
|
+
- `mock.module('pkg', { namedExports: {...} })` — module-level mocking (gated by inherited `AX_MOCK_AS_LAST_RESORT` + `AX_NO_FALSIFICATION_VIA_MOCKS`).
|
|
34
|
+
</Axiom>
|
|
35
|
+
|
|
36
|
+
<Axiom id="AX_HTTP_MOCK_AGENT_PATTERN">
|
|
37
|
+
For HTTP-heavy tests where injection is not feasible:
|
|
38
|
+
- Use `setupMockAgent()` from `utils/test/mock-http.ts`; call `cleanup()` in `afterEach`.
|
|
39
|
+
- Request-specific intercepts go INSIDE the case `SETUP` phase, not the shared hook.
|
|
40
|
+
- If the SUT uses `axios`, set `axios.defaults.adapter = 'fetch'` in shared setup so the mock agent sees the requests.
|
|
41
|
+
|
|
42
|
+
Default posture (per inherited `AX_MOCK_AS_LAST_RESORT`): inject an HTTP client / `request` port into the SUT and stub it with `mock.fn()`. Mock-agent harness is for code that already exists without an injection seam.
|
|
43
|
+
</Axiom>
|
|
44
|
+
|
|
45
|
+
<Axiom id="AX_SNAPSHOT_FILE_LOCATION">
|
|
46
|
+
`t.assert.snapshot()` / `t.assert.fileSnapshot()` outputs live in a dedicated `snapshots/` directory next to the test area. Updates go ONLY through the runner's snapshot-update flow under inherited `AX_SNAPSHOT_OPERATOR_CONFIRM` — manual edits to snapshot files are forbidden (they enable silent contract drift).
|
|
47
|
+
</Axiom>
|
|
48
|
+
|
|
49
|
+
</Belief_State>
|
|
50
|
+
|
|
51
|
+
<Test_Patterns>
|
|
52
|
+
<Pattern id="PT_DIRECT_SCALAR_CASE">
|
|
53
|
+
<Intent>Trivial scalar case without an opening brief: title + direct input + a single assertion already expose the scenario. Anchors omitted on the one-line TRIGGER and ASSERT phases per `AX_PHASE_ANCHORS`; kept only on SETUP because it has its own intent.</Intent>
|
|
54
|
+
<Snippet language="typescript">
|
|
55
|
+
```typescript
|
|
56
|
+
import { describe, it } from 'node:test';
|
|
57
|
+
import assert from 'node:assert/strict';
|
|
58
|
+
import { unwrap } from '#shared/result.ts';
|
|
59
|
+
|
|
60
|
+
describe('SlugBuilder', () => {
|
|
61
|
+
it('should build a lowercase slug from the title', async () => {
|
|
62
|
+
const input = { title: 'Hello World', separator: '-' };
|
|
63
|
+
const slug = await unwrap(builder.build(input));
|
|
64
|
+
assert.strictEqual(slug, 'hello-world');
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
</Snippet>
|
|
69
|
+
<Why>Title + direct input + single `strictEqual` make the scenario self-evident → brief skipped. Each phase is one statement with no hidden policy → anchors skipped. Phase grammar still readable for the next agent because the three lines naturally map SETUP/TRIGGER/ASSERT.</Why>
|
|
70
|
+
</Pattern>
|
|
71
|
+
|
|
72
|
+
<Pattern id="PT_ASYNC_RETRY_WITH_BRIEF">
|
|
73
|
+
<Intent>Async scenario with retry policy: opening brief records invariant + failure mode; phase anchors separate multi-line SETUP, the TRIGGER call, and the composite ASSERT with main + orthogonal observation.</Intent>
|
|
74
|
+
<Snippet language="typescript">
|
|
75
|
+
```typescript
|
|
76
|
+
import { describe, it, beforeEach, afterEach } from 'node:test';
|
|
77
|
+
import assert from 'node:assert/strict';
|
|
78
|
+
import { setupMockAgent } from '#utils/test/mock-http.ts';
|
|
79
|
+
|
|
80
|
+
describe('VendorClient', () => {
|
|
81
|
+
let mockEnv: ReturnType<typeof setupMockAgent>;
|
|
82
|
+
let client: VendorClient;
|
|
83
|
+
|
|
84
|
+
beforeEach(() => {
|
|
85
|
+
mockEnv = setupMockAgent();
|
|
86
|
+
client = new VendorClient({ baseUrl: 'https://vendor.test' });
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
afterEach(() => { mockEnv.cleanup(); });
|
|
90
|
+
|
|
91
|
+
it('should retry once and return the normalized payload', async () => {
|
|
92
|
+
// contract: one transient failure is tolerated before success is returned
|
|
93
|
+
// failure mode: do not wrap the returned result into a synthetic summary object
|
|
94
|
+
|
|
95
|
+
// #region START_FETCH_ITEM_SETUP_MOCKS
|
|
96
|
+
const url = 'https://vendor.test/items/42';
|
|
97
|
+
const tracker = mockEnv.interceptMultiple('GET', url, [
|
|
98
|
+
() => ({ status: 503, body: 'Service Unavailable' }),
|
|
99
|
+
() => ({ status: 200, body: { id: 42, name: 'Keyboard', inStock: true } }),
|
|
100
|
+
]);
|
|
101
|
+
// #endregion END_FETCH_ITEM_SETUP_MOCKS
|
|
102
|
+
|
|
103
|
+
const result = await client.fetchItem(url);
|
|
104
|
+
|
|
105
|
+
// #region START_FETCH_ITEM_ASSERT_RESULT
|
|
106
|
+
assert.deepStrictEqual(result, {
|
|
107
|
+
ok: true,
|
|
108
|
+
status: 200,
|
|
109
|
+
data: { id: 42, name: 'Keyboard', inStock: true },
|
|
110
|
+
});
|
|
111
|
+
assert.strictEqual(tracker.getAttemptCount(), 2);
|
|
112
|
+
// #endregion END_FETCH_ITEM_ASSERT_RESULT
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
</Snippet>
|
|
117
|
+
<Why>Brief reveals retry invariant; SETUP has multiple statements + intercept policy → anchored; TRIGGER is a single call → no anchor; ASSERT carries two related observations → anchored. Orthogonal `attemptCount` lives next to the main contract assertion without aggregation noise.</Why>
|
|
118
|
+
</Pattern>
|
|
119
|
+
|
|
120
|
+
<Pattern id="PT_ERROR_PATH_WITH_ASSERT_REJECTS">
|
|
121
|
+
<Intent>Error-path scenario: focused assertions via `assert.rejects()` + `assert.match()` on a stable message fragment; no `try/catch + flag` antipatterns.</Intent>
|
|
122
|
+
<Snippet language="typescript">
|
|
123
|
+
```typescript
|
|
124
|
+
import { describe, it } from 'node:test';
|
|
125
|
+
import assert from 'node:assert/strict';
|
|
126
|
+
|
|
127
|
+
describe('VendorClient#reserveStock', () => {
|
|
128
|
+
it('should reject with VendorStockError when upstream reports OUT_OF_STOCK', async () => {
|
|
129
|
+
// contract: domain error class + recognizable message preserves cause-chain
|
|
130
|
+
// failure mode: do NOT assert full error.message — it carries a dynamic correlation id
|
|
131
|
+
|
|
132
|
+
// #region START_RESERVE_STOCK_SETUP_MOCKS
|
|
133
|
+
const idempotencyKey = '550e8400-e29b-41d4-a716-446655440000';
|
|
134
|
+
mockEnv.interceptOnce('POST', '/v2/reservations', () => ({
|
|
135
|
+
status: 409,
|
|
136
|
+
body: { code: 'OUT_OF_STOCK' },
|
|
137
|
+
}));
|
|
138
|
+
// #endregion END_RESERVE_STOCK_SETUP_MOCKS
|
|
139
|
+
|
|
140
|
+
await assert.rejects(
|
|
141
|
+
() => client.reserveStock(idempotencyKey),
|
|
142
|
+
(error: unknown) => {
|
|
143
|
+
assert.ok(error instanceof VendorStockError);
|
|
144
|
+
assert.match((error as Error).message, /\[VendorClient#reserveStock\] .*OUT_OF_STOCK/);
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
</Snippet>
|
|
152
|
+
<Why>Combined TRIGGER+ASSERT via `assert.rejects` is intentional — forced phase split would yield an empty TRIGGER. The single `rejects` call needs no anchor; SETUP has multiple lines + interception policy → anchored.</Why>
|
|
153
|
+
</Pattern>
|
|
154
|
+
|
|
155
|
+
<Pattern id="PT_MOCK_FN_INTERACTION_CONTRACT">
|
|
156
|
+
<Intent>Native `mock.fn()` for tracking calls on injected collaborators + assertions on interaction contract without inspecting SUT internals.</Intent>
|
|
157
|
+
<Snippet language="typescript">
|
|
158
|
+
```typescript
|
|
159
|
+
import { describe, it, mock } from 'node:test';
|
|
160
|
+
import assert from 'node:assert/strict';
|
|
161
|
+
|
|
162
|
+
describe('OrderLifecycle#settleCharge', () => {
|
|
163
|
+
it('should persist order in PAID state and emit settled event after successful charge', async () => {
|
|
164
|
+
// observation focus: persistence call args + emitted event payload, not pipeline internals
|
|
165
|
+
|
|
166
|
+
// #region START_SETTLE_CHARGE_SETUP_DOUBLES
|
|
167
|
+
const repoSave = mock.fn(async (_order: Order) => {});
|
|
168
|
+
const onSettled = mock.fn();
|
|
169
|
+
const lifecycle = new OrderLifecycle({ saveOrder: repoSave });
|
|
170
|
+
lifecycle.on('settled', onSettled);
|
|
171
|
+
// #endregion END_SETTLE_CHARGE_SETUP_DOUBLES
|
|
172
|
+
|
|
173
|
+
await lifecycle.settleCharge('ord-1');
|
|
174
|
+
|
|
175
|
+
// #region START_SETTLE_CHARGE_ASSERT_INTERACTIONS
|
|
176
|
+
assert.strictEqual(repoSave.mock.callCount(), 1);
|
|
177
|
+
assert.deepStrictEqual(repoSave.mock.calls[0].arguments[0], {
|
|
178
|
+
id: 'ord-1',
|
|
179
|
+
state: 'PAID',
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
assert.strictEqual(onSettled.mock.callCount(), 1);
|
|
183
|
+
assert.deepStrictEqual(onSettled.mock.calls[0].arguments[0], {
|
|
184
|
+
type: 'order.settled',
|
|
185
|
+
detail: { orderId: 'ord-1' },
|
|
186
|
+
});
|
|
187
|
+
// #endregion END_SETTLE_CHARGE_ASSERT_INTERACTIONS
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
</Snippet>
|
|
192
|
+
<Why>Demonstrates `AX_PREFER_NATIVE_MOCK_FN` and contract-boundary observation of an emitted event (per inherited `AX_CONTRACT_OVER_IMPLEMENTATION`). Multi-statement SETUP and multi-statement ASSERT anchored; single-line TRIGGER is not.</Why>
|
|
193
|
+
</Pattern>
|
|
194
|
+
|
|
195
|
+
<Pattern id="PT_FIXTURE_AND_OBSERVE_AGGREGATION">
|
|
196
|
+
<Intent>Composite contract (generated text + observability milestone) where an OBSERVE phase normalizes result + side-effect log into one diagnostic slice. Justified case for aggregation per inherited `AX_DEFAULT_DIRECT_ASSERTION_PROTOCOL`.</Intent>
|
|
197
|
+
<Snippet language="typescript">
|
|
198
|
+
```typescript
|
|
199
|
+
import { describe, it, beforeEach } from 'node:test';
|
|
200
|
+
import assert from 'node:assert/strict';
|
|
201
|
+
import { readFileSync } from 'node:fs';
|
|
202
|
+
|
|
203
|
+
describe('CodegenPipeline#renderModule', () => {
|
|
204
|
+
let pipeline: CodegenPipeline;
|
|
205
|
+
|
|
206
|
+
beforeEach(() => {
|
|
207
|
+
pipeline = new CodegenPipeline({ logger: createMemoryLogger() });
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('should produce normalized output and emit a single info-level milestone log', async () => {
|
|
211
|
+
// contract: pipeline emits exactly one info log per successful render
|
|
212
|
+
// failure mode: do not snapshot scalar log count — it hides the actual milestone shape
|
|
213
|
+
|
|
214
|
+
const inputSpec = JSON.parse(readFileSync('test/fixtures/render-module.input.json', 'utf8'));
|
|
215
|
+
const output = await pipeline.renderModule(inputSpec);
|
|
216
|
+
|
|
217
|
+
// #region START_RENDER_MODULE_OBSERVE_AGGREGATE
|
|
218
|
+
// observation focus: text + milestone collapsed into one diff for a richer failure report
|
|
219
|
+
const actual = {
|
|
220
|
+
text: output.text,
|
|
221
|
+
milestones: pipeline.logger.records
|
|
222
|
+
.filter((r) => r.level === 'info')
|
|
223
|
+
.map((r) => r.message),
|
|
224
|
+
};
|
|
225
|
+
// #endregion END_RENDER_MODULE_OBSERVE_AGGREGATE
|
|
226
|
+
|
|
227
|
+
assert.deepStrictEqual(actual, {
|
|
228
|
+
text: '/* generated module */\nexport const value = 42;\n',
|
|
229
|
+
milestones: ['[CodegenPipeline#renderModule] [rendering → rendered] ok'],
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
```
|
|
234
|
+
</Snippet>
|
|
235
|
+
<Why>Aggregation in OBSERVE justified: a single `deepStrictEqual` shows drift in both text and milestones. SETUP and TRIGGER are one-liners → no anchors; OBSERVE has aggregation policy → anchored. Fixture read allowed, write forbidden (inherited `AX_FIXTURE_IO_POLICY`).</Why>
|
|
236
|
+
</Pattern>
|
|
237
|
+
</Test_Patterns>
|
|
238
|
+
|
|
239
|
+
<Anti_Patterns>
|
|
240
|
+
<Anti_Pattern id="AP_PRIVATE_INSPECTION">
|
|
241
|
+
<Bad>Test inspects `client._inflightCount` (protected/internal field) via `@ts-expect-error` to bypass encapsulation.</Bad>
|
|
242
|
+
<Why_Bad>Violates inherited `AX_CONTRACT_OVER_IMPLEMENTATION`. A refactor renaming `_inflightCount` to `_pending` breaks the test without changing the contract.</Why_Bad>
|
|
243
|
+
<Good>Subscribe to `client.on('reservation.requested', e => events.push(e))` and assert `events.length` + `events[0].idempotencyKey`. Public lifecycle event is the observable.</Good>
|
|
244
|
+
</Anti_Pattern>
|
|
245
|
+
|
|
246
|
+
<Anti_Pattern id="AP_TRY_CATCH_FLAG_ANTIPATTERN">
|
|
247
|
+
<Bad>`let threw = false; try { await client.reserveStock(''); } catch (e) { threw = true; assert.ok((e as Error).message.includes('idempotency')); } assert.ok(threw);`</Bad>
|
|
248
|
+
<Why_Bad>Manual try/catch + boolean flag instead of `assert.rejects()` (`AX_ASSERT_API_CHOICE`). `includes()` on error message (inherited `AX_PARTIAL_STRING_VIA_MATCH`). Test can silently pass if the code stops throwing — `threw` stays false, only fallback gate is `assert.ok(threw)`.</Why_Bad>
|
|
249
|
+
<Good>`await assert.rejects(() => client.reserveStock(''), (error) => { assert.match((error as Error).message, /idempotency.*empty|empty.*idempotency/i); return true; });`</Good>
|
|
250
|
+
</Anti_Pattern>
|
|
251
|
+
|
|
252
|
+
<Anti_Pattern id="AP_SHARED_MUTABLE_STATE_AND_NON_ENGLISH_NARRATION">
|
|
253
|
+
<Bad>`const cart = new Cart()` shared between tests; test names in Russian («добавляет товар»); `Шаг 1: добавляем` / `Шаг 2: проверяем` comments; second test depends on state from first.</Bad>
|
|
254
|
+
<Why_Bad>Shared mutable state breaks isolation (inherited `AX_NO_DESCRIBE_LET_PILEUP`); creates order dependency. Non-English names/comments (inherited `AX_ENGLISH_ONLY_NAMES_AND_COMMENTS`). Step-by-step human narration (inherited `AX_TESTS_NO_HUMAN_NARRATION`).</Why_Bad>
|
|
255
|
+
<Good>Single context factory `createCartContext(overrides?)` called inside each case; English titles like `should expose count = 1 after a single add`; direct assertions.</Good>
|
|
256
|
+
</Anti_Pattern>
|
|
257
|
+
|
|
258
|
+
<Anti_Pattern id="AP_SNAPSHOT_FOR_SCALAR">
|
|
259
|
+
<Bad>`it('returns 42', (t) => { const result = compute(); t.assert.snapshot(result); });`</Bad>
|
|
260
|
+
<Why_Bad>Snapshot for a scalar — violates inherited `AX_SNAPSHOT_USAGE_GATE`. Failure shows diff of snapshot blobs instead of «42 ≠ 41».</Why_Bad>
|
|
261
|
+
<Good>`assert.strictEqual(result, 42)` — direct, clear, no snapshot file overhead.</Good>
|
|
262
|
+
</Anti_Pattern>
|
|
263
|
+
</Anti_Patterns>
|
|
264
|
+
|
|
265
|
+
<Verification_Hooks>
|
|
266
|
+
<Hook id="HOOK_RUN_TESTS">
|
|
267
|
+
<Command>npm test</Command>
|
|
268
|
+
<Expected>Exit 0; all cases pass; no `.skip` / `.todo` without explicit deferred-ownership in the ticket.</Expected>
|
|
269
|
+
</Hook>
|
|
270
|
+
<Hook id="HOOK_RUN_SINGLE_FILE">
|
|
271
|
+
<Command>node --test path/to/subject.test.ts</Command>
|
|
272
|
+
<Expected>Exit 0; targeted file passes.</Expected>
|
|
273
|
+
</Hook>
|
|
274
|
+
<Hook id="HOOK_COVERAGE">
|
|
275
|
+
<Command>node --test --experimental-test-coverage</Command>
|
|
276
|
+
<Expected>Exit 0; report shows public methods with ≥1 happy + ≥1 boundary (when applicable) + ≥1 failure-path per inherited `AX_COVERAGE_BY_CONTRACT_NOT_BY_LINE`. Coverage cannot run → declare blocker EXPLICITLY.</Expected>
|
|
277
|
+
</Hook>
|
|
278
|
+
<Hook id="HOOK_TYPECHECK_BEFORE_TEST">
|
|
279
|
+
<Command>npx tsc --noEmit</Command>
|
|
280
|
+
<Expected>Exit 0; no TS errors.</Expected>
|
|
281
|
+
</Hook>
|
|
282
|
+
<Hook id="HOOK_NO_FORBIDDEN_TEST_PATTERNS">
|
|
283
|
+
<Purpose>Smoke-grep for forbidden idioms: human narration `Step N`, `includes(` on error message, manual try-catch flag.</Purpose>
|
|
284
|
+
<Command>rg --no-heading -n "Step \d|\.message.*\.includes\(|let\s+threw\s*=" -t ts --glob '**/*.test.ts'</Command>
|
|
285
|
+
<Expected>Empty on new/changed test files.</Expected>
|
|
286
|
+
</Hook>
|
|
287
|
+
</Verification_Hooks>
|
|
288
|
+
</NodeTestRules>
|