From 5b5c359294419ff68d06c3e9ddad0d5764fadfd6 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Fri, 14 Aug 2026 15:31:26 +0200 Subject: [PATCH 1/3] chore: update Guided Coding to 2.0.0 Signed-off-by: Kenny Pflug --- .../skills/guided-coding-finish-plan/SKILL.md | 65 +++++++++++++++ .../agents/openai.yaml | 7 ++ .../SKILL.md | 77 ++++++++++++++++++ .../agents/openai.yaml | 7 ++ .../skills/guided-coding-review-plan/SKILL.md | 38 +++++++++ .../agents/openai.yaml | 7 ++ .agents/skills/guided-coding-setup/SKILL.md | 69 ++++++++++++++++ .../guided-coding-setup/agents/openai.yaml | 7 ++ .../assets/ai-plans-AGENTS.md | 29 +++++++ .../guided-coding-write-deviations/SKILL.md | 62 ++++++++++++++ .../agents/openai.yaml | 7 ++ .../skills/guided-coding-write-plan/SKILL.md | 80 +++++++++++++++++++ .../agents/openai.yaml | 7 ++ .claude/settings.json | 5 ++ .../.idea/indexLayout.xml | 2 + AGENTS.md | 25 ++++-- ...adata.md => 2026-01-07-0707-1-metadata.md} | 0 ... 2026-01-07-1850-3-result-optimization.md} | 0 ...026-01-14-2050-5-functional-extensions.md} | 0 ...01-15-0645-7-minimal-api-serialization.md} | 0 ...nimal-api-metadata-for-success-results.md} | 0 ...0640-9-updated-minimal-api-integration.md} | 0 ...-2002-11-http-response-deserialization.md} | 0 ... => 2026-02-08-2035-11-plan-deviations.md} | 0 ...-02-11-1302-11-refactor-result-reading.md} | 0 ... => 2026-02-12-0944-13-mvc-integration.md} | 0 ...-14-0531-15-cloud-events-serialization.md} | 0 ...vents-reading-performance-optimization.md} | 0 ...859-15-cloud-events-write-optimization.md} | 0 ...254-15-cloud-events-write-streamlining.md} | 0 ... => 2026-02-21-1426-15-plan-deviations.md} | 0 ...> 2026-02-22-1613-15-final-code-review.md} | 0 ...17-http-write-integration-streamlining.md} | 0 ...d => 2026-02-25-0457-22-rename-summary.md} | 0 ... 2026-03-07-0714-24-validation-support.md} | 0 ...623-24-validation-context-optimization.md} | 0 ...-12-1835-24-normalization-optimization.md} | 0 ... => 2026-03-13-0715-24-plan-deviations.md} | 0 ...> 2026-03-15-1116-26-error-definitions.md} | 0 ...0-0526-28-child-validation-refactoring.md} | 0 ...-23-0540-30-validation-target-redesign.md} | 0 ...> 2026-03-24-0634-32-validation-checks.md} | 0 ... => 2026-03-29-1738-32-message-caching.md} | 0 ...1956-32-message-cache-key-optimization.md} | 0 ...05-0943-32-general-value-normalization.md} | 0 ...6-04-06-1212-32-validation-checkpoints.md} | 0 ...-04-06-2019-32-namespace-restructuring.md} | 0 ...tions-and-error-templates-optimization.md} | 0 ...-07-0519-24-validation-outcome-removal.md} | 0 ...-32-definition-and-check-restructuring.md} | 0 ...28-32-reduce-check-add-error-overloads.md} | 0 ...> 2026-04-09-2036-32-failure-overrides.md} | 0 ...md => 2026-04-11-0903-32-enhance-tests.md} | 0 ... => 2026-04-13-0447-32-plan-deviations.md} | 0 ...04-13-1730-34-configuration-validation.md} | 0 ... => 2026-04-14-0537-34-plan-deviations.md} | 0 ...-16-1901-37-validate-items-null-checks.md} | 0 ... => 2026-04-18-1131-40-openapi-support.md} | 0 ...=> 2026-04-19-2031-40-openapi-redesign.md} | 0 ...-20-1146-40-validation-error-contracts.md} | 0 ...26-04-29-1936-40-openapi-test-coverage.md} | 0 ...lity-for-built-in-validation-contracts.md} | 0 ... 2026-04-30-1720-40-openapi-exhaustive.md} | 0 ... => 2026-04-30-1926-40-plan-deviations.md} | 0 ...5-02-0510-43-openapi-source-generation.md} | 0 ...17-1956-43-improve-documentation-hints.md} | 0 ...05-21-0459-43-openapi-example-messages.md} | 0 ... => 2026-05-21-1926-43-plan-deviations.md} | 0 ...open-api-source-generation-mvc-support.md} | 0 ...pi-source-generation-mvc-customization.md} | 0 ...26-07-25-1002-52-decimal-metadata-kind.md} | 0 ...6-07-28-0559-55-metadata-restructuring.md} | 0 ...7-28-0559-metadata-round-trip-envelope.md} | 0 ... => 2026-07-29-1746-55-plan-deviations.md} | 0 ...8-fix-runtime-specific-number-metadata.md} | 0 ...1-0631-51-http-header-value-formatting.md} | 0 ...61-add-utf-8-floating-point-formatting.md} | 0 ...1-1617-63-add-stryker-mutation-testing.md} | 0 ... => 2026-08-01-1748-63-plan-deviations.md} | 0 ...6-resolve-first-stryker-survivor-queue.md} | 0 ...i-examples-for-non-constant-boundaries.md} | 0 ...cloud-events-extension-attribute-types.md} | 0 ...-try-format-canonical-zero-allocations.md} | 0 ... => 2026-08-02-1104-70-plan-deviations.md} | 0 ...v7.md => 2026-08-02-1258-72-is-uuid-v7.md} | 0 ...2026-08-02-1702-75-datetime-assertions.md} | 0 ...08-02-1811-78-aot-compatibility-checks.md} | 0 ... => 2026-08-03-0526-78-plan-deviations.md} | 0 ...026-08-03-2022-80-result-default-guard.md} | 0 ... => 2026-08-04-0334-80-plan-deviations.md} | 0 ...8-04-0444-82-enable-package-validation.md} | 0 ai-plans/AGENTS.md | 42 +++++----- src/Directory.Build.props | 5 +- tests/AGENTS.md | 6 +- .../PortableOpenApiSchemaTypeMapperTests.cs | 2 +- 95 files changed, 517 insertions(+), 32 deletions(-) create mode 100644 .agents/skills/guided-coding-finish-plan/SKILL.md create mode 100644 .agents/skills/guided-coding-finish-plan/agents/openai.yaml create mode 100644 .agents/skills/guided-coding-prepare-issue-for-plan/SKILL.md create mode 100644 .agents/skills/guided-coding-prepare-issue-for-plan/agents/openai.yaml create mode 100644 .agents/skills/guided-coding-review-plan/SKILL.md create mode 100644 .agents/skills/guided-coding-review-plan/agents/openai.yaml create mode 100644 .agents/skills/guided-coding-setup/SKILL.md create mode 100644 .agents/skills/guided-coding-setup/agents/openai.yaml create mode 100644 .agents/skills/guided-coding-setup/assets/ai-plans-AGENTS.md create mode 100644 .agents/skills/guided-coding-write-deviations/SKILL.md create mode 100644 .agents/skills/guided-coding-write-deviations/agents/openai.yaml create mode 100644 .agents/skills/guided-coding-write-plan/SKILL.md create mode 100644 .agents/skills/guided-coding-write-plan/agents/openai.yaml create mode 100644 .claude/settings.json rename ai-plans/{0001-metadata.md => 2026-01-07-0707-1-metadata.md} (100%) rename ai-plans/{0003-result-optimization.md => 2026-01-07-1850-3-result-optimization.md} (100%) rename ai-plans/{0005-functional-extensions.md => 2026-01-14-2050-5-functional-extensions.md} (100%) rename ai-plans/{0007-minimal-api-serialization.md => 2026-01-15-0645-7-minimal-api-serialization.md} (100%) rename ai-plans/{0009-minimal-api-metadata-for-success-results.md => 2026-01-17-1152-9-minimal-api-metadata-for-success-results.md} (100%) rename ai-plans/{0009-updated-minimal-api-integration.md => 2026-02-05-0640-9-updated-minimal-api-integration.md} (100%) rename ai-plans/{0011-0-http-response-deserialization.md => 2026-02-05-2002-11-http-response-deserialization.md} (100%) rename ai-plans/{0011-2-plan-deviations.md => 2026-02-08-2035-11-plan-deviations.md} (100%) rename ai-plans/{0011-1-refactor-result-reading.md => 2026-02-11-1302-11-refactor-result-reading.md} (100%) rename ai-plans/{0013-mvc-integration.md => 2026-02-12-0944-13-mvc-integration.md} (100%) rename ai-plans/{0015-0-cloud-events-serialization.md => 2026-02-14-0531-15-cloud-events-serialization.md} (100%) rename ai-plans/{0015-1-cloud-events-reading-performance-optimization.md => 2026-02-15-0705-15-cloud-events-reading-performance-optimization.md} (100%) rename ai-plans/{0015-2-cloud-events-write-optimization.md => 2026-02-15-0859-15-cloud-events-write-optimization.md} (100%) rename ai-plans/{0015-3-cloud-events-write-streamlining.md => 2026-02-15-1254-15-cloud-events-write-streamlining.md} (100%) rename ai-plans/{0015-5-plan-deviations.md => 2026-02-21-1426-15-plan-deviations.md} (100%) rename ai-plans/{0015-4-final-code-review.md => 2026-02-22-1613-15-final-code-review.md} (100%) rename ai-plans/{0017-HTTP-write-integration-streamlining.md => 2026-02-23-0720-17-http-write-integration-streamlining.md} (100%) rename ai-plans/{0022-rename-summary.md => 2026-02-25-0457-22-rename-summary.md} (100%) rename ai-plans/{0024-0-validation-support.md => 2026-03-07-0714-24-validation-support.md} (100%) rename ai-plans/{0024-1-validation-context-optimization.md => 2026-03-09-0623-24-validation-context-optimization.md} (100%) rename ai-plans/{0024-4-normalization-optimization.md => 2026-03-12-1835-24-normalization-optimization.md} (100%) rename ai-plans/{0024-5-plan-deviations.md => 2026-03-13-0715-24-plan-deviations.md} (100%) rename ai-plans/{0026-error-definitions.md => 2026-03-15-1116-26-error-definitions.md} (100%) rename ai-plans/{0028-child-validation-refactoring.md => 2026-03-20-0526-28-child-validation-refactoring.md} (100%) rename ai-plans/{0030-validation-target-redesign.md => 2026-03-23-0540-30-validation-target-redesign.md} (100%) rename ai-plans/{0032-0-validation-checks.md => 2026-03-24-0634-32-validation-checks.md} (100%) rename ai-plans/{0032-1-message-caching.md => 2026-03-29-1738-32-message-caching.md} (100%) rename ai-plans/{0032-2-message-cache-key-optimization.md => 2026-03-31-1956-32-message-cache-key-optimization.md} (100%) rename ai-plans/{0032-3-general-value-normalization.md => 2026-04-05-0943-32-general-value-normalization.md} (100%) rename ai-plans/{0032-4-validation-checkpoints.md => 2026-04-06-1212-32-validation-checkpoints.md} (100%) rename ai-plans/{0032-5-namespace-restructuring.md => 2026-04-06-2019-32-namespace-restructuring.md} (100%) rename ai-plans/{0024-2-options-and-error-templates-optimization.md => 2026-04-07-0519-24-options-and-error-templates-optimization.md} (100%) rename ai-plans/{0024-3-validation-outcome-removal.md => 2026-04-07-0519-24-validation-outcome-removal.md} (100%) rename ai-plans/{0032-6-definition-and-check-restructuring.md => 2026-04-07-1946-32-definition-and-check-restructuring.md} (100%) rename ai-plans/{0032-7-reduce-check-add-error-overloads.md => 2026-04-08-0428-32-reduce-check-add-error-overloads.md} (100%) rename ai-plans/{0032-8-failure-overrides.md => 2026-04-09-2036-32-failure-overrides.md} (100%) rename ai-plans/{0032-9-enhance-tests.md => 2026-04-11-0903-32-enhance-tests.md} (100%) rename ai-plans/{0032-plan-deviations.md => 2026-04-13-0447-32-plan-deviations.md} (100%) rename ai-plans/{0034-configuration-validation.md => 2026-04-13-1730-34-configuration-validation.md} (100%) rename ai-plans/{0034-plan-deviations.md => 2026-04-14-0537-34-plan-deviations.md} (100%) rename ai-plans/{0037-validate-items-null-checks.md => 2026-04-16-1901-37-validate-items-null-checks.md} (100%) rename ai-plans/{0040-0-openapi-support.md => 2026-04-18-1131-40-openapi-support.md} (100%) rename ai-plans/{0040-1-openapi-redesign.md => 2026-04-19-2031-40-openapi-redesign.md} (100%) rename ai-plans/{0040-2-validation-error-contracts.md => 2026-04-20-1146-40-validation-error-contracts.md} (100%) rename ai-plans/{0040-3-openapi-test-coverage.md => 2026-04-29-1936-40-openapi-test-coverage.md} (100%) rename ai-plans/{0040-4-native-aot-compatibility-for-built-in-validation-contracts.md => 2026-04-30-0608-40-native-aot-compatibility-for-built-in-validation-contracts.md} (100%) rename ai-plans/{0040-5-openapi-exhaustive.md => 2026-04-30-1720-40-openapi-exhaustive.md} (100%) rename ai-plans/{0040-6-plan-deviations.md => 2026-04-30-1926-40-plan-deviations.md} (100%) rename ai-plans/{0043-0-openapi-source-generation.md => 2026-05-02-0510-43-openapi-source-generation.md} (100%) rename ai-plans/{0043-1-improve-documentation-hints.md => 2026-05-17-1956-43-improve-documentation-hints.md} (100%) rename ai-plans/{0043-2-openapi-example-messages.md => 2026-05-21-0459-43-openapi-example-messages.md} (100%) rename ai-plans/{0043-3-plan-deviations.md => 2026-05-21-1926-43-plan-deviations.md} (100%) rename ai-plans/{0045-open-api-source-generation-mvc-support.md => 2026-05-22-1128-45-open-api-source-generation-mvc-support.md} (100%) rename ai-plans/{0047-openapi-source-generation-mvc-customization.md => 2026-05-23-0642-47-openapi-source-generation-mvc-customization.md} (100%) rename ai-plans/{0052-decimal-metadata-kind.md => 2026-07-25-1002-52-decimal-metadata-kind.md} (100%) rename ai-plans/{0055-0-metadata-restructuring.md => 2026-07-28-0559-55-metadata-restructuring.md} (100%) rename ai-plans/{xxx-metadata-round-trip-envelope.md => 2026-07-28-0559-metadata-round-trip-envelope.md} (100%) rename ai-plans/{0055-1-plan-deviations.md => 2026-07-29-1746-55-plan-deviations.md} (100%) rename ai-plans/{0058-fix-runtime-specific-number-metadata.md => 2026-07-30-0555-58-fix-runtime-specific-number-metadata.md} (100%) rename ai-plans/{0051-http-header-value-formatting.md => 2026-08-01-0631-51-http-header-value-formatting.md} (100%) rename ai-plans/{0061-add-utf-8-floating-point-formatting.md => 2026-08-01-1320-61-add-utf-8-floating-point-formatting.md} (100%) rename ai-plans/{0063-0-add-stryker-mutation-testing.md => 2026-08-01-1617-63-add-stryker-mutation-testing.md} (100%) rename ai-plans/{0063-1-plan-deviations.md => 2026-08-01-1748-63-plan-deviations.md} (100%) rename ai-plans/{0066-0-resolve-first-stryker-survivor-queue.md => 2026-08-01-2025-66-resolve-first-stryker-survivor-queue.md} (100%) rename ai-plans/{0057-0-openapi-examples-for-non-constant-boundaries.md => 2026-08-01-2133-57-openapi-examples-for-non-constant-boundaries.md} (100%) rename ai-plans/{0053-cloud-events-extension-attribute-types.md => 2026-08-02-0535-53-cloud-events-extension-attribute-types.md} (100%) rename ai-plans/{0070-0-try-format-canonical-zero-allocations.md => 2026-08-02-0917-70-try-format-canonical-zero-allocations.md} (100%) rename ai-plans/{0070-1-plan-deviations.md => 2026-08-02-1104-70-plan-deviations.md} (100%) rename ai-plans/{0072-is-uuid-v7.md => 2026-08-02-1258-72-is-uuid-v7.md} (100%) rename ai-plans/{0075-datetime-assertions.md => 2026-08-02-1702-75-datetime-assertions.md} (100%) rename ai-plans/{0078-0-aot-compatibility-checks.md => 2026-08-02-1811-78-aot-compatibility-checks.md} (100%) rename ai-plans/{0078-1-plan-deviations.md => 2026-08-03-0526-78-plan-deviations.md} (100%) rename ai-plans/{0080-0-result-default-guard.md => 2026-08-03-2022-80-result-default-guard.md} (100%) rename ai-plans/{0080-1-plan-deviations.md => 2026-08-04-0334-80-plan-deviations.md} (100%) rename ai-plans/{0082-enable-package-validation.md => 2026-08-04-0444-82-enable-package-validation.md} (100%) diff --git a/.agents/skills/guided-coding-finish-plan/SKILL.md b/.agents/skills/guided-coding-finish-plan/SKILL.md new file mode 100644 index 00000000..cc33a748 --- /dev/null +++ b/.agents/skills/guided-coding-finish-plan/SKILL.md @@ -0,0 +1,65 @@ +--- +description: Validate and freeze a Guided Coding plan, then optionally publish it to its tracker issue. Run only when explicitly requested by the user. +license: MIT +metadata: + github-path: skills/guided-coding-finish-plan + github-ref: refs/heads/main + github-repo: https://github.com/feO2x/guided-coding + github-tree-sha: 991dbf3c4edc54ba093457a75a53b23a52e9501b +name: guided-coding-finish-plan +--- +# Finish a Plan + +Finish the plan named by the user. If none is named, proceed only when exactly one uncommitted plan +draft exists in `ai-plans/`; otherwise ask for its path. + +## 1. Validate + +Read the repository instructions and confirm that: + +- The filename is either + `YYYY-MM-DD-HHMM--.md` or, without an issue, + `YYYY-MM-DD-HHMM-.md`. +- Variable filename segments contain only lowercase ASCII letters, digits, and single hyphens, and + do not start or end with a hyphen. +- The file starts with `# Title`, followed by exactly `## Rationale`, + `## Acceptance Criteria`, and `## Technical Details`, in that order. +- Every acceptance criterion is an unticked task (`- [ ]`). +- Referenced plan documents exist, and claims about existing source files are accurate. Paths for + files the plan intends to create are valid references when identified as planned work. + +Report validation failures. Fix them only after the user agrees; the Planning Phase is still open +until the plan is committed. + +## 2. Commit and freeze + +Inspect `git status` and the staged diff. Preserve unrelated working-tree and staged changes. Stage +the plan if needed, then use a path-limited commit so the commit contains only the plan file. Verify +the resulting commit's file list before continuing; if it contains anything else, stop and report +the problem without rewriting history. Follow repository commit conventions and do not push. The +successful commit ends the Planning Phase and freezes the plan. + +## 3. Optionally publish the first plan + +The first plan for a tracked issue may become that issue's description. Follow-up plans are not +published there. + +Use the tracker and target project documented by the repository. If neither is documented, infer +them only when the git remote and tracker clearly agree, state the inferred target, and ask the +user to confirm it. If the project has no tracker, skip this step. + +Read the current issue title and description before asking to publish. Explain that publishing +replaces the complete issue description. If it is non-empty, show or summarize what would be +replaced and require explicit confirmation to overwrite it. Publish the plan body only. For GitHub: + +```sh +gh issue edit --body-file +``` + +If the plan and issue titles differ, report it without renaming either. Treat the tracker body as +a publication snapshot, never as the source from which the committed plan is amended. Never change +the tracker without the user's approval. + +## 4. Report + +Report the committed plan path and state that its Planning Phase has ended and it is now frozen. diff --git a/.agents/skills/guided-coding-finish-plan/agents/openai.yaml b/.agents/skills/guided-coding-finish-plan/agents/openai.yaml new file mode 100644 index 00000000..fcf66cf1 --- /dev/null +++ b/.agents/skills/guided-coding-finish-plan/agents/openai.yaml @@ -0,0 +1,7 @@ +interface: + display_name: "Finish Guided Coding Plan" + short_description: "Validate, commit, and freeze a completed plan" + default_prompt: "Use $guided-coding-finish-plan to validate and finish this plan." + +policy: + allow_implicit_invocation: false diff --git a/.agents/skills/guided-coding-prepare-issue-for-plan/SKILL.md b/.agents/skills/guided-coding-prepare-issue-for-plan/SKILL.md new file mode 100644 index 00000000..6763d009 --- /dev/null +++ b/.agents/skills/guided-coding-prepare-issue-for-plan/SKILL.md @@ -0,0 +1,77 @@ +--- +description: Create an empty tracker issue and a clean local branch for a Guided Coding plan. Run only when explicitly requested by the user. +license: MIT +metadata: + github-path: skills/guided-coding-prepare-issue-for-plan + github-ref: refs/heads/main + github-repo: https://github.com/feO2x/guided-coding + github-tree-sha: 67b76327f9be129189afe9d8390f64044d3d69c5 +name: guided-coding-prepare-issue-for-plan +--- +# Prepare an Issue for a Plan + +Create the issue and branch only. Leave planning to the user and the planning conversation. + +## 1. Check prerequisites + +Read the repository instructions. Before creating the issue: + +1. Run `git status`. If the worktree has uncommitted changes, stop before creating the issue. +2. Determine the repository's default branch. +3. Determine the issue tracker and target project from repository instructions. If they are not + documented, infer them only when the git remote and tracker clearly agree, such as `gh` with a + GitHub remote. State the inferred target and ask the user to confirm it. +4. When a tracker exists, confirm that its CLI is available and authenticated for the target + project. Check its help rather than guessing flags. + +Use the title supplied by the user. If none is provided, ask for a short title. Derive a lowercase, +hyphen-separated topic of at most four words. + +Example: `Support cancelled events` becomes `cancelled-events`. + +## 2. Update the default branch + +Switch to the default branch and update it before creating the external issue: + +```sh +git switch +git pull --ff-only +``` + +Stop if either command fails. + +## 3. Create the issue + +Create the issue with the agreed title and an empty description. Do not add a summary, +acceptance criteria, or placeholder text. Use the tracker's documented CLI. For GitHub: + +```sh +gh issue create --title "" --body "" +``` + +Read the identifier and URL from the command output. Normalize the identifier for filenames and +branches: convert it to lowercase, remove a leading `#`, replace each run of characters other than +`a-z` and `0-9` with one hyphen, trim leading and trailing hyphens, and do not add zero padding. Stop +if normalization produces an empty identifier. + +If the project has no issue tracker, skip issue creation and use the topic alone for the branch +and later plan filename. + +## 4. Create the branch + +Create `<issue-id>-<topic>` or, without an issue, `<topic>`. Validate the complete name before +creating it: + +```sh +git check-ref-format --branch "<branch-name>" +git switch -c <branch-name> +``` + +Stop if the intended branch already exists; do not reuse or rename it implicitly. If validation or +branch creation fails after the issue was created, do not close or delete the issue automatically. +Report the issue and the failure so the user can choose the recovery action. + +## 5. Report + +Report the issue identifier and URL, when present, and the branch name. Then stop. Do not write +the plan. diff --git a/.agents/skills/guided-coding-prepare-issue-for-plan/agents/openai.yaml b/.agents/skills/guided-coding-prepare-issue-for-plan/agents/openai.yaml new file mode 100644 index 00000000..e62f985c --- /dev/null +++ b/.agents/skills/guided-coding-prepare-issue-for-plan/agents/openai.yaml @@ -0,0 +1,7 @@ +interface: + display_name: "Prepare Guided Coding Issue" + short_description: "Create a tracker issue and clean planning branch" + default_prompt: "Use $guided-coding-prepare-issue-for-plan to create an issue and branch for this work." + +policy: + allow_implicit_invocation: false diff --git a/.agents/skills/guided-coding-review-plan/SKILL.md b/.agents/skills/guided-coding-review-plan/SKILL.md new file mode 100644 index 00000000..ab15bd6f --- /dev/null +++ b/.agents/skills/guided-coding-review-plan/SKILL.md @@ -0,0 +1,38 @@ +--- +description: Review a completed Guided Coding plan draft against the codebase before its Planning Phase ends. Run only when explicitly requested by the user. +license: MIT +metadata: + github-path: skills/guided-coding-review-plan + github-ref: refs/heads/main + github-repo: https://github.com/feO2x/guided-coding + github-tree-sha: f02d971824425244e54bb006335f8b2885cb7b6b +name: guided-coding-review-plan +--- +# Review a Plan + +Review the plan named by the user. If none is named, proceed only when exactly one uncommitted plan +draft exists in `ai-plans/`; otherwise ask for its path. Report findings only; never edit the plan +or any other file. + +Read the plan, applicable repository instructions, the code it discusses, and every earlier +document for the same issue, including legacy filenames. Verify assertions about existing types, +members, files, APIs, and extension points. Do not reject files or types merely because the plan +intends to create them. + +Amongst other things, check for: + +- **Acceptance criteria:** Each criterion describes an observable, verifiable outcome rather + than an implementation step. Behavior changes require appropriate automated test coverage. +- **Verifiability:** The required feedback loops exist in repository instructions, build files, + scripts, or CI configuration. +- **Technical decisions:** Data structures, abstractions, and framework mechanisms fit the + surrounding architecture without unnecessary indirection. +- **Level of detail:** The plan records non-obvious decisions and interactions without + prescribing routine implementation. +- **Omissions:** Judge relevant risks such as error handling, compatibility, migration, + performance, security boundaries, and observability against the change at hand. +- **History:** A follow-up plan explicitly identifies and explains any earlier decisions it + supersedes. + +Say explicitly when there are no findings. If the plan is already frozen, note that corrections +require a follow-up plan rather than edits. diff --git a/.agents/skills/guided-coding-review-plan/agents/openai.yaml b/.agents/skills/guided-coding-review-plan/agents/openai.yaml new file mode 100644 index 00000000..ff558693 --- /dev/null +++ b/.agents/skills/guided-coding-review-plan/agents/openai.yaml @@ -0,0 +1,7 @@ +interface: + display_name: "Review Guided Coding Plan" + short_description: "Review a plan draft against the repository" + default_prompt: "Use $guided-coding-review-plan to review this plan draft against the repository." + +policy: + allow_implicit_invocation: false diff --git a/.agents/skills/guided-coding-setup/SKILL.md b/.agents/skills/guided-coding-setup/SKILL.md new file mode 100644 index 00000000..35114263 --- /dev/null +++ b/.agents/skills/guided-coding-setup/SKILL.md @@ -0,0 +1,69 @@ +--- +description: Set up or upgrade Guided Coding repository instructions, plan storage, and documented feedback-loop commands. Run only when explicitly requested by the user. +license: MIT +metadata: + github-path: skills/guided-coding-setup + github-ref: refs/heads/main + github-repo: https://github.com/feO2x/guided-coding + github-tree-sha: 45dc5336407039c7a5c1422eb37ad7069a11724f +name: guided-coding-setup +--- +# Set Up Guided Coding + +Set up or upgrade Guided Coding in the current repository. Preserve project-specific instructions +and user-authored content. Never modify existing plan or Plan Deviations documents. + +## 1. Inspect the repository + +Read the root `AGENTS.md` when present, `ai-plans/AGENTS.md` when present, nested instruction +files, build manifests, task runners, scripts, and CI configuration. + +Identify feedback loops and their exact commands. Look for: + +- compilers, transpilers, type checkers, static analyzers, and linters; +- unit, integration, end-to-end, coverage, and mutation tests; +- benchmarks and performance tests; and +- dependency, secret, container, and source-code security scans. + +Do not invent commands or list tools merely because they are common for the detected language. +Prefer the repository's documented entry points. When a command is safe, non-destructive, and +reasonably bounded, run it to detect stale instructions. Do not claim that a command passed unless +it was executed successfully; report why any documented command was not run. If existing Guided +Coding instructions conflict with this version and resolving them would discard a project-specific +decision, ask before editing. + +## 2. Update the root instructions + +Create `AGENTS.md` if it does not exist. Otherwise make the smallest idempotent update that +preserves unrelated sections. + +Ensure it contains: + +1. `## When you implement a plan`, stating that: + - plans in `ai-plans/` are frozen after their Planning Phase; + - only acceptance criteria may change, from `- [ ]` to `- [x]`; + - a criterion is checked only after the implementation and relevant feedback loops verify it; + - unmet criteria remain unchecked and their wording is never changed; and + - material departures from explicit plan decisions require a Plan Deviations document rather + than edits to the frozen plan. +2. `## Feedback loops`, listing each repository-confirmed command and what it verifies. State + plainly when no automated feedback-loop command can be confirmed. +3. `## How to write plans`, linking to `ai-plans/AGENTS.md`. +4. `## This is your space` as the final section, inviting agents to record noteworthy repository + discoveries for later discussion. Preserve any notes already in that section. + +Consolidate equivalent existing sections instead of adding duplicates. + +## 3. Update the plan record + +Create `ai-plans/` when missing. Create or update `ai-plans/AGENTS.md` from +`assets/ai-plans-AGENTS.md`. + +Keep the version marker and portable Guided Coding rules current while preserving repository- +specific additions and notes about legacy plan filenames. Do not rename, rewrite, or reorganize +historical documents during an upgrade. + +## 4. Report + +Report the files created or updated, the feedback loops documented, which commands were executed +and their results, why any were not run, and any unresolved conflicts. Then stop. diff --git a/.agents/skills/guided-coding-setup/agents/openai.yaml b/.agents/skills/guided-coding-setup/agents/openai.yaml new file mode 100644 index 00000000..88a9600e --- /dev/null +++ b/.agents/skills/guided-coding-setup/agents/openai.yaml @@ -0,0 +1,7 @@ +interface: + display_name: "Guided Coding Setup" + short_description: "Set up or upgrade Guided Coding in a repository" + default_prompt: "Use $guided-coding-setup to set up or upgrade Guided Coding in this repository." + +policy: + allow_implicit_invocation: false diff --git a/.agents/skills/guided-coding-setup/assets/ai-plans-AGENTS.md b/.agents/skills/guided-coding-setup/assets/ai-plans-AGENTS.md new file mode 100644 index 00000000..06fdc2e8 --- /dev/null +++ b/.agents/skills/guided-coding-setup/assets/ai-plans-AGENTS.md @@ -0,0 +1,29 @@ +# AGENTS.md for AI plans + +<!-- guided-coding-version: 2.0.0 --> + +This directory is the append-only record of how work in this repository was planned and how it +turned out. It follows the [Guided Coding](https://kenny-codes.net/docs/guided-coding/) approach. + +Plan filenames use `YYYY-MM-DD-HHMM-<issue-id>-<kebab-case-description>.md`, timestamped in UTC +when written. Omit the issue segment when work has no tracker issue. Plan Deviations filenames use +`YYYY-MM-DD-HHMM-<issue-id>-plan-deviations.md`, or the corresponding topic when there is no issue. +Do not rename historical documents solely to conform to the current format. + +Plans are frozen when their Planning Phase ends. From then on, the only permitted edit is checking +an acceptance criterion (`- [ ]` to `- [x]`) after the implementation and relevant feedback loops +verify it. Never reword, add, remove, or reorder criteria in a frozen plan. Plan Deviations +documents are frozen once committed. Never rename or delete a plan or Plan Deviations document. + +Every plan starts with `# Title` followed by exactly `## Rationale`, `## Acceptance Criteria`, and +`## Technical Details`, in that order. Acceptance criteria are observable, verifiable outcomes +written as unchecked Markdown tasks. Technical Details record important decisions, constraints, +affected components, and non-obvious interactions without prescribing routine implementation. + +Use a follow-up plan to correct or supersede a frozen plan. Always write a Plan Deviations document +when at least one follow-up plan exists, summarizing which earlier decisions they superseded and the final +outcome. Also use a Plan Deviations document when the final implementation materially changes or +rejects an explicit plan decision. Plan Deviations must identify the compared plans and describe +what was planned, what changed, why it changed, the final implementation, and the impact. + +Before writing a plan or Plan Deviations document, use the corresponding Guided Coding skill. diff --git a/.agents/skills/guided-coding-write-deviations/SKILL.md b/.agents/skills/guided-coding-write-deviations/SKILL.md new file mode 100644 index 00000000..b4474b73 --- /dev/null +++ b/.agents/skills/guided-coding-write-deviations/SKILL.md @@ -0,0 +1,62 @@ +--- +description: Decide whether a completed Guided Coding cycle needs a Plan Deviations document and write it when required. Run only when explicitly requested by the user. +license: MIT +metadata: + github-path: skills/guided-coding-write-deviations + github-ref: refs/heads/main + github-repo: https://github.com/feO2x/guided-coding + github-tree-sha: da0a168bf4404594f4c22291bc8f07f3c1f8432c +name: guided-coding-write-deviations +--- +# Write Plan Deviations + +## Inspect + +- Read all plans for the current work, oldest first. +- Determine the implementation range from its PR/MR, or the target branch's merge base with the + implementation branch. +- Inspect its commits, all committed/staged/unstaged changes, and relevant earlier history. Verify + every document and source-file reference. + +## Decide + +Create the Plan Deviation Document if: + +- **Follow-up plans exist:** summarize changes from the first plan so it and this document explain + the complete work. +- **The implementation materially changes or rejects an explicit plan decision** about an + acceptance outcome; public contract or data model; architectural or component boundary; security + or performance constraint; or another design decision future maintainers need. + +Exclude routine choices the plans left open. A documented but unmet acceptance criterion remains +incomplete unless an accepted follow-up plan explicitly supersedes it. + +Otherwise, with one plan and no material deviations, report that conclusion and create nothing. + +## Write + +Use `YYYY-MM-DD-HHMM-<issue-id>-plan-deviations.md`; without a tracker issue, use +`YYYY-MM-DD-HHMM-<topic>-plan-deviations.md`. Get UTC from the shell, never the conversation: +`date -u +%F-%H%M`, or PowerShell `(Get-Date).ToUniversalTime().ToString("yyyy-MM-dd-HHmm")`. +Normalize issue identifiers like the related plans. Resolve the full unused path. If any plan or +Plan Deviations document already uses it, report the collision and stop; never overwrite or reuse. + +Write: + +1. `# <issue-id or topic> Plan Deviations`. +2. An opening paragraph naming every compared plan by exact filename and the implementation branch, + and identifying unimplemented plans. +3. `## Summary`: what held up and how many material decisions changed. +4. `## Changes Across Follow-Up Plans`, when applicable: one numbered `###` per superseded decision + with **Original decision**, **Superseded by** (exact filename and replacement), **Why**, and + **Final outcome**. +5. `## Deviations From the Accepted Plans`, when applicable: one numbered `###` per material + implementation deviation with **Plan decision** (exact filename and decision), **Implemented**, + **Why** (required), and **Impact** (trade-offs, consequences, or deferred work; omit only if none). + +Name affected types, members, and files. Exclude work matching the accepted plans. If all follow-up +plans match, say so in the Summary and omit `## Deviations From the Accepted Plans`. + +## Stop + +Report the path. Do not commit, publish, or create/update a PR/MR. The user reviews and finalizes it. diff --git a/.agents/skills/guided-coding-write-deviations/agents/openai.yaml b/.agents/skills/guided-coding-write-deviations/agents/openai.yaml new file mode 100644 index 00000000..85074e1d --- /dev/null +++ b/.agents/skills/guided-coding-write-deviations/agents/openai.yaml @@ -0,0 +1,7 @@ +interface: + display_name: "Write Guided Coding Plan Deviations" + short_description: "Summarize follow-up plans and code deviations" + default_prompt: "Use $guided-coding-write-deviations to compare the implementation with its plans." + +policy: + allow_implicit_invocation: false diff --git a/.agents/skills/guided-coding-write-plan/SKILL.md b/.agents/skills/guided-coding-write-plan/SKILL.md new file mode 100644 index 00000000..baebc38c --- /dev/null +++ b/.agents/skills/guided-coding-write-plan/SKILL.md @@ -0,0 +1,80 @@ +--- +description: Write a Guided Coding plan or follow-up plan after its approach has been discussed. Run only when explicitly requested by the user. +license: MIT +metadata: + github-path: skills/guided-coding-write-plan + github-ref: refs/heads/main + github-repo: https://github.com/feO2x/guided-coding + github-tree-sha: a4f2460317872e658ae15c3074140d8f432e75d3 +name: guided-coding-write-plan +--- +# Write a Plan + +Write the agreed approach into `ai-plans/`. Do not use this skill to begin or replace the +planning conversation. Read the repository instructions, including `ai-plans/AGENTS.md`, first. + +After writing, report the plan path and stop. + +## File name + +Use: + +```text +YYYY-MM-DD-HHMM-<issue-id>-<kebab-case-description>.md +``` + +Omit `<issue-id>-` when the work has no tracker issue. Obtain the current UTC timestamp from the +shell; never infer it from conversation context. For example: + +```sh +date -u +%F-%H%M +``` + +In PowerShell, use `(Get-Date).ToUniversalTime().ToString("yyyy-MM-dd-HHmm")`. + +Normalize the tracker identifier for filenames: convert it to lowercase, remove a leading `#`, +replace each run of characters other than `a-z` and `0-9` with one hyphen, trim leading and trailing +hyphens, and do not add zero padding. Stop if normalization produces an empty identifier. If the +work requires an issue but none exists, stop and suggest the Guided Coding issue-preparation skill +instead of creating a temporary filename. + +Resolve the complete destination path before writing and confirm it does not exist. Never +overwrite or reuse an existing plan or Plan Deviations document; report a collision and stop. + +## Structure + +Start with `# Title`, followed by exactly these `##` sections in order: + +1. `## Rationale`: briefly state the problem and overarching goal. +2. `## Acceptance Criteria`: list observable, verifiable outcomes as unticked Markdown tasks + (`- [ ]`). Describe results, not implementation steps. +3. `## Technical Details`: record important decisions, constraints, affected components, and + non-obvious interactions. Give a senior engineer room to implement. + +Use minimal code examples only when they clarify an important contract, such as an API +signature, interface, or DTO shape. Say whether an example is exact or illustrative when +unclear. Avoid method bodies, step-by-step instructions, exhaustive file lists, and routine +background. + +When behavior changes, require appropriate automated test coverage in the acceptance criteria. +Require benchmarks only when performance is a material risk or requirement. + +Discover feedback loops from repository instructions, build files, scripts, and CI configuration. +Look for: + +- compilers, type checkers, static analyzers, and linters; +- automated tests, code coverage, and mutation tests; +- benchmarks and performance tests; and +- security scans. + +Use the relevant feedback loops to make acceptance criteria verifiable. Do not require every +available loop or claim that tooling exists without confirming it. If the change requires a +missing feedback loop, make adding it explicit in the plan. + +## Follow-up plans + +Use the same format and issue identifier with a later timestamp. Read every earlier document for +the issue. In the Rationale, name the plans this one follows by exact filename. + +Record only the changed decisions and outcomes. State exactly which earlier decisions this plan +supersedes and why. Do not repeat unchanged decisions or contradict earlier plans silently. diff --git a/.agents/skills/guided-coding-write-plan/agents/openai.yaml b/.agents/skills/guided-coding-write-plan/agents/openai.yaml new file mode 100644 index 00000000..62788057 --- /dev/null +++ b/.agents/skills/guided-coding-write-plan/agents/openai.yaml @@ -0,0 +1,7 @@ +interface: + display_name: "Write Guided Coding Plan" + short_description: "Write an agreed approach as a plan document" + default_prompt: "Use $guided-coding-write-plan to write the agreed plan for this work." + +policy: + allow_implicit_invocation: false diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 00000000..d531d965 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,5 @@ +{ + "enabledPlugins": { + "guided-coding@guided-coding": true + } +} diff --git a/.idea/.idea.Light.PortableResults/.idea/indexLayout.xml b/.idea/.idea.Light.PortableResults/.idea/indexLayout.xml index 1619d777..d8e7fe73 100644 --- a/.idea/.idea.Light.PortableResults/.idea/indexLayout.xml +++ b/.idea/.idea.Light.PortableResults/.idea/indexLayout.xml @@ -2,6 +2,8 @@ <project version="4"> <component name="UserContentModel"> <attachedFolders> + <Path>.agents</Path> + <Path>.claude</Path> <Path>.github</Path> <Path>ai-plans</Path> </attachedFolders> diff --git a/AGENTS.md b/AGENTS.md index 5a648fea..75ec111c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,18 +2,29 @@ Light.PortableResults is a lightweight, high-performance library implementing the Result Pattern for .NET. It stands out for reducing allocations and being able to serialize and deserialize results across different protocols (HTTP via RFC-9457, gRPC, Asynchronous Messaging). Extensibility is less important than performance. -## Implementation rules +## When you implement a plan -Plans typically have acceptance criteria with check boxes. Check each box when you are finished with the corresponding criterion. +Plans in `ai-plans/` are frozen after their Planning Phase. The only permitted change to a frozen plan is checking an acceptance criterion (`- [ ]` to `- [x]`), and only after the implementation and the relevant feedback loops verify it. Leave unmet criteria unchecked and never change their wording. If the implementation materially departs from an explicit plan decision, write a Plan Deviations document instead of editing the frozen plan. + +## Feedback loops + +Run all commands from the repository root. + +- `dotnet test Light.PortableResults.slnx` - builds the solution and runs all test projects. See ./tests/AGENTS.md for how to write tests. +- `dotnet test Light.PortableResults.slnx -- --coverage --coverage-settings "$PWD/coverage.runsettings" --coverage-output-format cobertura` - additionally collects line coverage. Merge the per-project reports with `reportgenerator -reports:'**/*.cobertura.xml' -targetdir:./coverage-merged -reporttypes:'Cobertura;TextSummary'`. Line coverage must stay at or above 95%. +- `dotnet pack ./Light.PortableResults.slnx -c Release` - Release build (warnings are errors) plus package validation of the public API shape against the published baseline. This is exactly the CI gate; see Package Validation for details and the offline escape hatch. +- `dotnet test ./tests/Light.PortableResults.Tests/Light.PortableResults.Tests.csproj --configuration Release -p:PortableResultsAssetTargetFramework=netstandard2.0` - CI gate that runs the core test suite against the netstandard2.0 asset. +- `dotnet publish ./samples/NativeAotMovieRating/NativeAotMovieRating.csproj -c Release -r linux-x64` - CI gate for the Native AOT compatibility claim. Requires a platform linker (clang/gcc), see https://aka.ms/nativeaot-prerequisites. +- Benchmarks: `dotnet run -c Release --project benchmarks/Benchmarks -- --filter <glob>` (BenchmarkDotNet switcher). Run only for changes where performance is a material risk or requirement. +- Mutation testing: `dotnet tool restore`, then `UsePublicSigningKey=false dotnet stryker -p <project>` from the repository root (the environment variable opts out of public signing, which Stryker 4.16.0 cannot re-emit). Local, on-demand use only; see ./tests/AGENTS.md for configuration, baselines, and triage rules. + +No dependency, secret, container, or source-code security scans are configured in this repository. ## General Rules for the Code Base In our Directory.Build.props files in this solution, the following rules are defined: -- Implicit usings or global usings are not allowed - use explicit using statements for clarity. -- Use C# 14 across all projects. - The library is not published in a stable version yet, you can make breaking changes. -- `<TreatWarningsAsErrors>` is enabled in Release builds, so your code changes must not generate warnings. - When a type or method is properly encapsulated, make it public. We don't know how callers would like to use this library. When some types are internal, this might make it hard for callers to access these in tests or when making configuration changes. Prefer public APIs over internal ones. - Use Conventional Commits messages. Decide whether a commit title is enough or a commit body is required. @@ -59,10 +70,10 @@ The `0.7.0` cleanup was done this way: the two intentional `MetadataKind.Array`/ Read ./tests/AGENTS.md for details about how to write tests. -## Plan Rules +## How to write plans Read ./ai-plans/AGENTS.md for details on how to write plans. -## Here is Your Space +## This is your space If you encounter something worth noting while you are working on this code base, write it down here in this section. Once you are finished, I will discuss it with you, and we can decide where to put your notes. diff --git a/ai-plans/0001-metadata.md b/ai-plans/2026-01-07-0707-1-metadata.md similarity index 100% rename from ai-plans/0001-metadata.md rename to ai-plans/2026-01-07-0707-1-metadata.md diff --git a/ai-plans/0003-result-optimization.md b/ai-plans/2026-01-07-1850-3-result-optimization.md similarity index 100% rename from ai-plans/0003-result-optimization.md rename to ai-plans/2026-01-07-1850-3-result-optimization.md diff --git a/ai-plans/0005-functional-extensions.md b/ai-plans/2026-01-14-2050-5-functional-extensions.md similarity index 100% rename from ai-plans/0005-functional-extensions.md rename to ai-plans/2026-01-14-2050-5-functional-extensions.md diff --git a/ai-plans/0007-minimal-api-serialization.md b/ai-plans/2026-01-15-0645-7-minimal-api-serialization.md similarity index 100% rename from ai-plans/0007-minimal-api-serialization.md rename to ai-plans/2026-01-15-0645-7-minimal-api-serialization.md diff --git a/ai-plans/0009-minimal-api-metadata-for-success-results.md b/ai-plans/2026-01-17-1152-9-minimal-api-metadata-for-success-results.md similarity index 100% rename from ai-plans/0009-minimal-api-metadata-for-success-results.md rename to ai-plans/2026-01-17-1152-9-minimal-api-metadata-for-success-results.md diff --git a/ai-plans/0009-updated-minimal-api-integration.md b/ai-plans/2026-02-05-0640-9-updated-minimal-api-integration.md similarity index 100% rename from ai-plans/0009-updated-minimal-api-integration.md rename to ai-plans/2026-02-05-0640-9-updated-minimal-api-integration.md diff --git a/ai-plans/0011-0-http-response-deserialization.md b/ai-plans/2026-02-05-2002-11-http-response-deserialization.md similarity index 100% rename from ai-plans/0011-0-http-response-deserialization.md rename to ai-plans/2026-02-05-2002-11-http-response-deserialization.md diff --git a/ai-plans/0011-2-plan-deviations.md b/ai-plans/2026-02-08-2035-11-plan-deviations.md similarity index 100% rename from ai-plans/0011-2-plan-deviations.md rename to ai-plans/2026-02-08-2035-11-plan-deviations.md diff --git a/ai-plans/0011-1-refactor-result-reading.md b/ai-plans/2026-02-11-1302-11-refactor-result-reading.md similarity index 100% rename from ai-plans/0011-1-refactor-result-reading.md rename to ai-plans/2026-02-11-1302-11-refactor-result-reading.md diff --git a/ai-plans/0013-mvc-integration.md b/ai-plans/2026-02-12-0944-13-mvc-integration.md similarity index 100% rename from ai-plans/0013-mvc-integration.md rename to ai-plans/2026-02-12-0944-13-mvc-integration.md diff --git a/ai-plans/0015-0-cloud-events-serialization.md b/ai-plans/2026-02-14-0531-15-cloud-events-serialization.md similarity index 100% rename from ai-plans/0015-0-cloud-events-serialization.md rename to ai-plans/2026-02-14-0531-15-cloud-events-serialization.md diff --git a/ai-plans/0015-1-cloud-events-reading-performance-optimization.md b/ai-plans/2026-02-15-0705-15-cloud-events-reading-performance-optimization.md similarity index 100% rename from ai-plans/0015-1-cloud-events-reading-performance-optimization.md rename to ai-plans/2026-02-15-0705-15-cloud-events-reading-performance-optimization.md diff --git a/ai-plans/0015-2-cloud-events-write-optimization.md b/ai-plans/2026-02-15-0859-15-cloud-events-write-optimization.md similarity index 100% rename from ai-plans/0015-2-cloud-events-write-optimization.md rename to ai-plans/2026-02-15-0859-15-cloud-events-write-optimization.md diff --git a/ai-plans/0015-3-cloud-events-write-streamlining.md b/ai-plans/2026-02-15-1254-15-cloud-events-write-streamlining.md similarity index 100% rename from ai-plans/0015-3-cloud-events-write-streamlining.md rename to ai-plans/2026-02-15-1254-15-cloud-events-write-streamlining.md diff --git a/ai-plans/0015-5-plan-deviations.md b/ai-plans/2026-02-21-1426-15-plan-deviations.md similarity index 100% rename from ai-plans/0015-5-plan-deviations.md rename to ai-plans/2026-02-21-1426-15-plan-deviations.md diff --git a/ai-plans/0015-4-final-code-review.md b/ai-plans/2026-02-22-1613-15-final-code-review.md similarity index 100% rename from ai-plans/0015-4-final-code-review.md rename to ai-plans/2026-02-22-1613-15-final-code-review.md diff --git a/ai-plans/0017-HTTP-write-integration-streamlining.md b/ai-plans/2026-02-23-0720-17-http-write-integration-streamlining.md similarity index 100% rename from ai-plans/0017-HTTP-write-integration-streamlining.md rename to ai-plans/2026-02-23-0720-17-http-write-integration-streamlining.md diff --git a/ai-plans/0022-rename-summary.md b/ai-plans/2026-02-25-0457-22-rename-summary.md similarity index 100% rename from ai-plans/0022-rename-summary.md rename to ai-plans/2026-02-25-0457-22-rename-summary.md diff --git a/ai-plans/0024-0-validation-support.md b/ai-plans/2026-03-07-0714-24-validation-support.md similarity index 100% rename from ai-plans/0024-0-validation-support.md rename to ai-plans/2026-03-07-0714-24-validation-support.md diff --git a/ai-plans/0024-1-validation-context-optimization.md b/ai-plans/2026-03-09-0623-24-validation-context-optimization.md similarity index 100% rename from ai-plans/0024-1-validation-context-optimization.md rename to ai-plans/2026-03-09-0623-24-validation-context-optimization.md diff --git a/ai-plans/0024-4-normalization-optimization.md b/ai-plans/2026-03-12-1835-24-normalization-optimization.md similarity index 100% rename from ai-plans/0024-4-normalization-optimization.md rename to ai-plans/2026-03-12-1835-24-normalization-optimization.md diff --git a/ai-plans/0024-5-plan-deviations.md b/ai-plans/2026-03-13-0715-24-plan-deviations.md similarity index 100% rename from ai-plans/0024-5-plan-deviations.md rename to ai-plans/2026-03-13-0715-24-plan-deviations.md diff --git a/ai-plans/0026-error-definitions.md b/ai-plans/2026-03-15-1116-26-error-definitions.md similarity index 100% rename from ai-plans/0026-error-definitions.md rename to ai-plans/2026-03-15-1116-26-error-definitions.md diff --git a/ai-plans/0028-child-validation-refactoring.md b/ai-plans/2026-03-20-0526-28-child-validation-refactoring.md similarity index 100% rename from ai-plans/0028-child-validation-refactoring.md rename to ai-plans/2026-03-20-0526-28-child-validation-refactoring.md diff --git a/ai-plans/0030-validation-target-redesign.md b/ai-plans/2026-03-23-0540-30-validation-target-redesign.md similarity index 100% rename from ai-plans/0030-validation-target-redesign.md rename to ai-plans/2026-03-23-0540-30-validation-target-redesign.md diff --git a/ai-plans/0032-0-validation-checks.md b/ai-plans/2026-03-24-0634-32-validation-checks.md similarity index 100% rename from ai-plans/0032-0-validation-checks.md rename to ai-plans/2026-03-24-0634-32-validation-checks.md diff --git a/ai-plans/0032-1-message-caching.md b/ai-plans/2026-03-29-1738-32-message-caching.md similarity index 100% rename from ai-plans/0032-1-message-caching.md rename to ai-plans/2026-03-29-1738-32-message-caching.md diff --git a/ai-plans/0032-2-message-cache-key-optimization.md b/ai-plans/2026-03-31-1956-32-message-cache-key-optimization.md similarity index 100% rename from ai-plans/0032-2-message-cache-key-optimization.md rename to ai-plans/2026-03-31-1956-32-message-cache-key-optimization.md diff --git a/ai-plans/0032-3-general-value-normalization.md b/ai-plans/2026-04-05-0943-32-general-value-normalization.md similarity index 100% rename from ai-plans/0032-3-general-value-normalization.md rename to ai-plans/2026-04-05-0943-32-general-value-normalization.md diff --git a/ai-plans/0032-4-validation-checkpoints.md b/ai-plans/2026-04-06-1212-32-validation-checkpoints.md similarity index 100% rename from ai-plans/0032-4-validation-checkpoints.md rename to ai-plans/2026-04-06-1212-32-validation-checkpoints.md diff --git a/ai-plans/0032-5-namespace-restructuring.md b/ai-plans/2026-04-06-2019-32-namespace-restructuring.md similarity index 100% rename from ai-plans/0032-5-namespace-restructuring.md rename to ai-plans/2026-04-06-2019-32-namespace-restructuring.md diff --git a/ai-plans/0024-2-options-and-error-templates-optimization.md b/ai-plans/2026-04-07-0519-24-options-and-error-templates-optimization.md similarity index 100% rename from ai-plans/0024-2-options-and-error-templates-optimization.md rename to ai-plans/2026-04-07-0519-24-options-and-error-templates-optimization.md diff --git a/ai-plans/0024-3-validation-outcome-removal.md b/ai-plans/2026-04-07-0519-24-validation-outcome-removal.md similarity index 100% rename from ai-plans/0024-3-validation-outcome-removal.md rename to ai-plans/2026-04-07-0519-24-validation-outcome-removal.md diff --git a/ai-plans/0032-6-definition-and-check-restructuring.md b/ai-plans/2026-04-07-1946-32-definition-and-check-restructuring.md similarity index 100% rename from ai-plans/0032-6-definition-and-check-restructuring.md rename to ai-plans/2026-04-07-1946-32-definition-and-check-restructuring.md diff --git a/ai-plans/0032-7-reduce-check-add-error-overloads.md b/ai-plans/2026-04-08-0428-32-reduce-check-add-error-overloads.md similarity index 100% rename from ai-plans/0032-7-reduce-check-add-error-overloads.md rename to ai-plans/2026-04-08-0428-32-reduce-check-add-error-overloads.md diff --git a/ai-plans/0032-8-failure-overrides.md b/ai-plans/2026-04-09-2036-32-failure-overrides.md similarity index 100% rename from ai-plans/0032-8-failure-overrides.md rename to ai-plans/2026-04-09-2036-32-failure-overrides.md diff --git a/ai-plans/0032-9-enhance-tests.md b/ai-plans/2026-04-11-0903-32-enhance-tests.md similarity index 100% rename from ai-plans/0032-9-enhance-tests.md rename to ai-plans/2026-04-11-0903-32-enhance-tests.md diff --git a/ai-plans/0032-plan-deviations.md b/ai-plans/2026-04-13-0447-32-plan-deviations.md similarity index 100% rename from ai-plans/0032-plan-deviations.md rename to ai-plans/2026-04-13-0447-32-plan-deviations.md diff --git a/ai-plans/0034-configuration-validation.md b/ai-plans/2026-04-13-1730-34-configuration-validation.md similarity index 100% rename from ai-plans/0034-configuration-validation.md rename to ai-plans/2026-04-13-1730-34-configuration-validation.md diff --git a/ai-plans/0034-plan-deviations.md b/ai-plans/2026-04-14-0537-34-plan-deviations.md similarity index 100% rename from ai-plans/0034-plan-deviations.md rename to ai-plans/2026-04-14-0537-34-plan-deviations.md diff --git a/ai-plans/0037-validate-items-null-checks.md b/ai-plans/2026-04-16-1901-37-validate-items-null-checks.md similarity index 100% rename from ai-plans/0037-validate-items-null-checks.md rename to ai-plans/2026-04-16-1901-37-validate-items-null-checks.md diff --git a/ai-plans/0040-0-openapi-support.md b/ai-plans/2026-04-18-1131-40-openapi-support.md similarity index 100% rename from ai-plans/0040-0-openapi-support.md rename to ai-plans/2026-04-18-1131-40-openapi-support.md diff --git a/ai-plans/0040-1-openapi-redesign.md b/ai-plans/2026-04-19-2031-40-openapi-redesign.md similarity index 100% rename from ai-plans/0040-1-openapi-redesign.md rename to ai-plans/2026-04-19-2031-40-openapi-redesign.md diff --git a/ai-plans/0040-2-validation-error-contracts.md b/ai-plans/2026-04-20-1146-40-validation-error-contracts.md similarity index 100% rename from ai-plans/0040-2-validation-error-contracts.md rename to ai-plans/2026-04-20-1146-40-validation-error-contracts.md diff --git a/ai-plans/0040-3-openapi-test-coverage.md b/ai-plans/2026-04-29-1936-40-openapi-test-coverage.md similarity index 100% rename from ai-plans/0040-3-openapi-test-coverage.md rename to ai-plans/2026-04-29-1936-40-openapi-test-coverage.md diff --git a/ai-plans/0040-4-native-aot-compatibility-for-built-in-validation-contracts.md b/ai-plans/2026-04-30-0608-40-native-aot-compatibility-for-built-in-validation-contracts.md similarity index 100% rename from ai-plans/0040-4-native-aot-compatibility-for-built-in-validation-contracts.md rename to ai-plans/2026-04-30-0608-40-native-aot-compatibility-for-built-in-validation-contracts.md diff --git a/ai-plans/0040-5-openapi-exhaustive.md b/ai-plans/2026-04-30-1720-40-openapi-exhaustive.md similarity index 100% rename from ai-plans/0040-5-openapi-exhaustive.md rename to ai-plans/2026-04-30-1720-40-openapi-exhaustive.md diff --git a/ai-plans/0040-6-plan-deviations.md b/ai-plans/2026-04-30-1926-40-plan-deviations.md similarity index 100% rename from ai-plans/0040-6-plan-deviations.md rename to ai-plans/2026-04-30-1926-40-plan-deviations.md diff --git a/ai-plans/0043-0-openapi-source-generation.md b/ai-plans/2026-05-02-0510-43-openapi-source-generation.md similarity index 100% rename from ai-plans/0043-0-openapi-source-generation.md rename to ai-plans/2026-05-02-0510-43-openapi-source-generation.md diff --git a/ai-plans/0043-1-improve-documentation-hints.md b/ai-plans/2026-05-17-1956-43-improve-documentation-hints.md similarity index 100% rename from ai-plans/0043-1-improve-documentation-hints.md rename to ai-plans/2026-05-17-1956-43-improve-documentation-hints.md diff --git a/ai-plans/0043-2-openapi-example-messages.md b/ai-plans/2026-05-21-0459-43-openapi-example-messages.md similarity index 100% rename from ai-plans/0043-2-openapi-example-messages.md rename to ai-plans/2026-05-21-0459-43-openapi-example-messages.md diff --git a/ai-plans/0043-3-plan-deviations.md b/ai-plans/2026-05-21-1926-43-plan-deviations.md similarity index 100% rename from ai-plans/0043-3-plan-deviations.md rename to ai-plans/2026-05-21-1926-43-plan-deviations.md diff --git a/ai-plans/0045-open-api-source-generation-mvc-support.md b/ai-plans/2026-05-22-1128-45-open-api-source-generation-mvc-support.md similarity index 100% rename from ai-plans/0045-open-api-source-generation-mvc-support.md rename to ai-plans/2026-05-22-1128-45-open-api-source-generation-mvc-support.md diff --git a/ai-plans/0047-openapi-source-generation-mvc-customization.md b/ai-plans/2026-05-23-0642-47-openapi-source-generation-mvc-customization.md similarity index 100% rename from ai-plans/0047-openapi-source-generation-mvc-customization.md rename to ai-plans/2026-05-23-0642-47-openapi-source-generation-mvc-customization.md diff --git a/ai-plans/0052-decimal-metadata-kind.md b/ai-plans/2026-07-25-1002-52-decimal-metadata-kind.md similarity index 100% rename from ai-plans/0052-decimal-metadata-kind.md rename to ai-plans/2026-07-25-1002-52-decimal-metadata-kind.md diff --git a/ai-plans/0055-0-metadata-restructuring.md b/ai-plans/2026-07-28-0559-55-metadata-restructuring.md similarity index 100% rename from ai-plans/0055-0-metadata-restructuring.md rename to ai-plans/2026-07-28-0559-55-metadata-restructuring.md diff --git a/ai-plans/xxx-metadata-round-trip-envelope.md b/ai-plans/2026-07-28-0559-metadata-round-trip-envelope.md similarity index 100% rename from ai-plans/xxx-metadata-round-trip-envelope.md rename to ai-plans/2026-07-28-0559-metadata-round-trip-envelope.md diff --git a/ai-plans/0055-1-plan-deviations.md b/ai-plans/2026-07-29-1746-55-plan-deviations.md similarity index 100% rename from ai-plans/0055-1-plan-deviations.md rename to ai-plans/2026-07-29-1746-55-plan-deviations.md diff --git a/ai-plans/0058-fix-runtime-specific-number-metadata.md b/ai-plans/2026-07-30-0555-58-fix-runtime-specific-number-metadata.md similarity index 100% rename from ai-plans/0058-fix-runtime-specific-number-metadata.md rename to ai-plans/2026-07-30-0555-58-fix-runtime-specific-number-metadata.md diff --git a/ai-plans/0051-http-header-value-formatting.md b/ai-plans/2026-08-01-0631-51-http-header-value-formatting.md similarity index 100% rename from ai-plans/0051-http-header-value-formatting.md rename to ai-plans/2026-08-01-0631-51-http-header-value-formatting.md diff --git a/ai-plans/0061-add-utf-8-floating-point-formatting.md b/ai-plans/2026-08-01-1320-61-add-utf-8-floating-point-formatting.md similarity index 100% rename from ai-plans/0061-add-utf-8-floating-point-formatting.md rename to ai-plans/2026-08-01-1320-61-add-utf-8-floating-point-formatting.md diff --git a/ai-plans/0063-0-add-stryker-mutation-testing.md b/ai-plans/2026-08-01-1617-63-add-stryker-mutation-testing.md similarity index 100% rename from ai-plans/0063-0-add-stryker-mutation-testing.md rename to ai-plans/2026-08-01-1617-63-add-stryker-mutation-testing.md diff --git a/ai-plans/0063-1-plan-deviations.md b/ai-plans/2026-08-01-1748-63-plan-deviations.md similarity index 100% rename from ai-plans/0063-1-plan-deviations.md rename to ai-plans/2026-08-01-1748-63-plan-deviations.md diff --git a/ai-plans/0066-0-resolve-first-stryker-survivor-queue.md b/ai-plans/2026-08-01-2025-66-resolve-first-stryker-survivor-queue.md similarity index 100% rename from ai-plans/0066-0-resolve-first-stryker-survivor-queue.md rename to ai-plans/2026-08-01-2025-66-resolve-first-stryker-survivor-queue.md diff --git a/ai-plans/0057-0-openapi-examples-for-non-constant-boundaries.md b/ai-plans/2026-08-01-2133-57-openapi-examples-for-non-constant-boundaries.md similarity index 100% rename from ai-plans/0057-0-openapi-examples-for-non-constant-boundaries.md rename to ai-plans/2026-08-01-2133-57-openapi-examples-for-non-constant-boundaries.md diff --git a/ai-plans/0053-cloud-events-extension-attribute-types.md b/ai-plans/2026-08-02-0535-53-cloud-events-extension-attribute-types.md similarity index 100% rename from ai-plans/0053-cloud-events-extension-attribute-types.md rename to ai-plans/2026-08-02-0535-53-cloud-events-extension-attribute-types.md diff --git a/ai-plans/0070-0-try-format-canonical-zero-allocations.md b/ai-plans/2026-08-02-0917-70-try-format-canonical-zero-allocations.md similarity index 100% rename from ai-plans/0070-0-try-format-canonical-zero-allocations.md rename to ai-plans/2026-08-02-0917-70-try-format-canonical-zero-allocations.md diff --git a/ai-plans/0070-1-plan-deviations.md b/ai-plans/2026-08-02-1104-70-plan-deviations.md similarity index 100% rename from ai-plans/0070-1-plan-deviations.md rename to ai-plans/2026-08-02-1104-70-plan-deviations.md diff --git a/ai-plans/0072-is-uuid-v7.md b/ai-plans/2026-08-02-1258-72-is-uuid-v7.md similarity index 100% rename from ai-plans/0072-is-uuid-v7.md rename to ai-plans/2026-08-02-1258-72-is-uuid-v7.md diff --git a/ai-plans/0075-datetime-assertions.md b/ai-plans/2026-08-02-1702-75-datetime-assertions.md similarity index 100% rename from ai-plans/0075-datetime-assertions.md rename to ai-plans/2026-08-02-1702-75-datetime-assertions.md diff --git a/ai-plans/0078-0-aot-compatibility-checks.md b/ai-plans/2026-08-02-1811-78-aot-compatibility-checks.md similarity index 100% rename from ai-plans/0078-0-aot-compatibility-checks.md rename to ai-plans/2026-08-02-1811-78-aot-compatibility-checks.md diff --git a/ai-plans/0078-1-plan-deviations.md b/ai-plans/2026-08-03-0526-78-plan-deviations.md similarity index 100% rename from ai-plans/0078-1-plan-deviations.md rename to ai-plans/2026-08-03-0526-78-plan-deviations.md diff --git a/ai-plans/0080-0-result-default-guard.md b/ai-plans/2026-08-03-2022-80-result-default-guard.md similarity index 100% rename from ai-plans/0080-0-result-default-guard.md rename to ai-plans/2026-08-03-2022-80-result-default-guard.md diff --git a/ai-plans/0080-1-plan-deviations.md b/ai-plans/2026-08-04-0334-80-plan-deviations.md similarity index 100% rename from ai-plans/0080-1-plan-deviations.md rename to ai-plans/2026-08-04-0334-80-plan-deviations.md diff --git a/ai-plans/0082-enable-package-validation.md b/ai-plans/2026-08-04-0444-82-enable-package-validation.md similarity index 100% rename from ai-plans/0082-enable-package-validation.md rename to ai-plans/2026-08-04-0444-82-enable-package-validation.md diff --git a/ai-plans/AGENTS.md b/ai-plans/AGENTS.md index 1f190a6d..06fdc2e8 100644 --- a/ai-plans/AGENTS.md +++ b/ai-plans/AGENTS.md @@ -1,29 +1,29 @@ # AGENTS.md for AI plans -*Version 1.1* +<!-- guided-coding-version: 2.0.0 --> -This directory contains Markdown plans and plan deviation records, plus this instruction file. These documents follow the [Guided Coding](https://kenny-codes.net/docs/guided-coding/) approach. +This directory is the append-only record of how work in this repository was planned and how it +turned out. It follows the [Guided Coding](https://kenny-codes.net/docs/guided-coding/) approach. -File names begin with the four-digit GitHub issue number and a short kebab-case description. When an issue has multiple documents, insert a zero-based sequence after the issue number, for example `0015-0-feature.md`, `0015-1-optimization.md`, and `0015-2-plan-deviations.md`. Do not rename or restructure completed historical plans solely to conform to the current rules. +Plan filenames use `YYYY-MM-DD-HHMM-<issue-id>-<kebab-case-description>.md`, timestamped in UTC +when written. Omit the issue segment when work has no tracker issue. Plan Deviations filenames use +`YYYY-MM-DD-HHMM-<issue-id>-plan-deviations.md`, or the corresponding topic when there is no issue. +Do not rename historical documents solely to conform to the current format. -## How to Write Plans +Plans are frozen when their Planning Phase ends. From then on, the only permitted edit is checking +an acceptance criterion (`- [ ]` to `- [x]`) after the implementation and relevant feedback loops +verify it. Never reword, add, remove, or reorder criteria in a frozen plan. Plan Deviations +documents are frozen once committed. Never rename or delete a plan or Plan Deviations document. -1. Every plan starts with a `# Title` and contains exactly three sections, in this order: `## Rationale`, `## Acceptance Criteria`, and `## Technical Details`. -2. The Rationale briefly describes the problem and the overarching goal of addressing it. Keep it to one or two short paragraphs unless additional context is necessary to understand the change. -3. Acceptance Criteria contains observable and verifiable outcomes using Markdown task-list items (`- [ ]`). Describe what must be true when the work is complete, not the individual implementation steps. -4. Technical Details records the important implementation decisions, constraints, affected components, and non-obvious interactions. Include enough information for an implementer to understand the intended design without prescribing the complete implementation. Assume the implementer is a senior software engineer. -5. Use code examples in Technical Details when they define an important contract more clearly and concisely than prose, such as API signatures, central interfaces, or DTO shapes. Keep them minimal, omit implementation bodies and routine context, and identify them as exact or illustrative when this is not obvious. Avoid step-by-step instructions, exhaustive file lists, and background knowledge expected of a senior software engineer. -6. When behavior changes, include an acceptance criterion requiring appropriate automated test coverage. Describe specific test cases in Technical Details only when they are not obvious from the acceptance criteria. -7. Include microbenchmarks only for changes where performance is a relevant risk or requirement. Add them to Acceptance Criteria and describe benchmark scenarios in Technical Details only when those scenarios are not self-evident. -8. Keep plans concise. Prefer decisions, constraints, and outcomes to explanations of routine implementation work. +Every plan starts with `# Title` followed by exactly `## Rationale`, `## Acceptance Criteria`, and +`## Technical Details`, in that order. Acceptance criteria are observable, verifiable outcomes +written as unchecked Markdown tasks. Technical Details record important decisions, constraints, +affected components, and non-obvious interactions without prescribing routine implementation. -Use these rules for follow-up plans, too. +Use a follow-up plan to correct or supersede a frozen plan. Always write a Plan Deviations document +when at least one follow-up plan exists, summarizing which earlier decisions they superseded and the final +outcome. Also use a Plan Deviations document when the final implementation materially changes or +rejects an explicit plan decision. Plan Deviations must identify the compared plans and describe +what was planned, what changed, why it changed, the final implementation, and the impact. -## How to Write Plan Deviation Files - -When users find substantial issues in the Guiding Phase and create new plans to fix these, a single plan deviation file sums up the differences from the original plan and its follow-up plans. They do not use the same structure as plans, you can use any format that clearly communicates the differences. Often, a feature-based approach is appropriate. - -While the format is flexible, include the following information: - -- Reference every plan included in the comparison. -- Describe what was originally specified, what the codebase actually does, and why the change was made. +Before writing a plan or Plan Deviations document, use the corresponding Guided Coding skill. diff --git a/src/Directory.Build.props b/src/Directory.Build.props index f9eefa58..e4bc0e83 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -45,9 +45,12 @@ The release workflow passes AssemblyOriginatorKeyFile as a global property, which this file cannot override; the guard therefore reads that property, leaves UsePublicSigningKey empty and never sets PublicSign, so released packages stay privately signed. Do not guard on SignAssembly - the release sets that too. + The guard also respects an externally set UsePublicSigningKey so that Stryker runs can opt out of signing: + Stryker 4.16.0 cannot re-emit mutated assemblies with a public-only key (CS7032), so tests/AGENTS.md + invokes it with UsePublicSigningKey=false in the environment. --> <PropertyGroup> - <UsePublicSigningKey Condition="'$(AssemblyOriginatorKeyFile)' == ''">true</UsePublicSigningKey> + <UsePublicSigningKey Condition="'$(AssemblyOriginatorKeyFile)' == '' and '$(UsePublicSigningKey)' == ''">true</UsePublicSigningKey> <SignAssembly Condition="'$(UsePublicSigningKey)' == 'true'">true</SignAssembly> <PublicSign Condition="'$(UsePublicSigningKey)' == 'true'">true</PublicSign> <AssemblyOriginatorKeyFile Condition="'$(UsePublicSigningKey)' == 'true'">$(MSBuildThisFileDirectory)../Light.PortableResults.Public.snk</AssemblyOriginatorKeyFile> diff --git a/tests/AGENTS.md b/tests/AGENTS.md index 6663359d..0b060046 100644 --- a/tests/AGENTS.md +++ b/tests/AGENTS.md @@ -32,13 +32,15 @@ Always run from the repository root: Stryker reads `stryker-config.json` only fr Revisit `coverage-analysis: off` when Stryker's MTP per-test coverage support ([#3516](https://github.com/stryker-mutator/stryker-net/pull/3516)) ships and proves trustworthy; it is the main lever on run time because it avoids running the full discovered test set for every mutant. +Always run Stryker with `UsePublicSigningKey=false` in the environment. Ordinary builds public-sign with the committed `Light.PortableResults.Public.snk` (see `src/Directory.Build.props`), and Stryker 4.16.0 cannot re-emit mutated assemblies with a public-only key — the mutation compile fails with `error CS7032` before any mutant is tested. The props file deliberately lets an externally set `UsePublicSigningKey` win over its default, so the environment variable opts the whole run (initial build, project analysis, and mutation compiles) out of signing. Stryker has no MSBuild-property passthrough, which is why this goes through the environment rather than a `-p:` argument. + ```shell # One project — sufficient for the four small projects -dotnet stryker -p Light.PortableResults.AspNetCore.Shared.csproj +UsePublicSigningKey=false dotnet stryker -p Light.PortableResults.AspNetCore.Shared.csproj # One-file configuration smoke check (~5 minutes) # Expect: 2,675 tests, 67 killed, 0 NoCoverage, 100.00% -dotnet stryker -p Light.PortableResults.csproj -m '**/Result.cs' +UsePublicSigningKey=false dotnet stryker -p Light.PortableResults.csproj -m '**/Result.cs' ``` The smoke-check vector is tied to the current `Result.cs` and its tests; update it after intentional changes alter the mutant inventory. All mutants surviving with a 0.00% score suggests fallback to the `vstest` runner, while any non-zero `NoCoverage` count suggests `coverage-analysis: off` was lost. diff --git a/tests/Light.PortableResults.AspNetCore.OpenApi.Tests/PortableOpenApiSchemaTypeMapperTests.cs b/tests/Light.PortableResults.AspNetCore.OpenApi.Tests/PortableOpenApiSchemaTypeMapperTests.cs index ee28a937..dfd0cc85 100644 --- a/tests/Light.PortableResults.AspNetCore.OpenApi.Tests/PortableOpenApiSchemaTypeMapperTests.cs +++ b/tests/Light.PortableResults.AspNetCore.OpenApi.Tests/PortableOpenApiSchemaTypeMapperTests.cs @@ -11,7 +11,7 @@ namespace Light.PortableResults.AspNetCore.OpenApi.Tests; public sealed class PortableOpenApiSchemaTypeMapperTests { - // The schema column of the vocabulary table in ai-plans/0055-metadata-restructuring.md is normative, and + // The schema column of the vocabulary table in ai-plans/2026-07-28-0559-55-metadata-restructuring.md is normative, and // this is its only guard: the validation source generator emits Map<T>() calls rather than keeping a table // of its own. Keying the expectations on MetadataKind means declaring a kind fails this test until its // schema has been decided, instead of silently degrading to a schema without a type. From 97a41db5f918ea11b54f4267d1d7a5301a16c42b Mon Sep 17 00:00:00 2001 From: Kenny Pflug <kenny.pflug@live.de> Date: Fri, 14 Aug 2026 16:05:08 +0200 Subject: [PATCH 2/3] docs: tighten repository instructions --- AGENTS.md | 76 ++++++++------------------ src/Directory.Build.props | 4 +- tests/AGENTS.md | 112 +++++--------------------------------- tests/mutation-testing.md | 51 +++++++++++++++++ 4 files changed, 89 insertions(+), 154 deletions(-) create mode 100644 tests/mutation-testing.md diff --git a/AGENTS.md b/AGENTS.md index 75ec111c..ea0144e5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,79 +1,49 @@ -# Root Agents.md +# Repository instructions -Light.PortableResults is a lightweight, high-performance library implementing the Result Pattern for .NET. It stands out for reducing allocations and being able to serialize and deserialize results across different protocols (HTTP via RFC-9457, gRPC, Asynchronous Messaging). Extensibility is less important than performance. +Light.PortableResults is a high-performance Result Pattern library for .NET with typed round trips, e.g., for RFC 9457 HTTP responses and CloudEvents. Prioritize low allocations and throughput over extensibility. ## When you implement a plan -Plans in `ai-plans/` are frozen after their Planning Phase. The only permitted change to a frozen plan is checking an acceptance criterion (`- [ ]` to `- [x]`), and only after the implementation and the relevant feedback loops verify it. Leave unmet criteria unchecked and never change their wording. If the implementation materially departs from an explicit plan decision, write a Plan Deviations document instead of editing the frozen plan. +Plans in `ai-plans/` are frozen after their Planning Phase. The only permitted change to a frozen plan is checking an acceptance criterion (`- [ ]` to `- [x]`), and only after the implementation and relevant feedback loops verify it. Leave unmet criteria unchecked and never change their wording. If the implementation materially departs from an explicit plan decision, write a Plan Deviations document instead of editing the frozen plan. ## Feedback loops Run all commands from the repository root. -- `dotnet test Light.PortableResults.slnx` - builds the solution and runs all test projects. See ./tests/AGENTS.md for how to write tests. -- `dotnet test Light.PortableResults.slnx -- --coverage --coverage-settings "$PWD/coverage.runsettings" --coverage-output-format cobertura` - additionally collects line coverage. Merge the per-project reports with `reportgenerator -reports:'**/*.cobertura.xml' -targetdir:./coverage-merged -reporttypes:'Cobertura;TextSummary'`. Line coverage must stay at or above 95%. -- `dotnet pack ./Light.PortableResults.slnx -c Release` - Release build (warnings are errors) plus package validation of the public API shape against the published baseline. This is exactly the CI gate; see Package Validation for details and the offline escape hatch. -- `dotnet test ./tests/Light.PortableResults.Tests/Light.PortableResults.Tests.csproj --configuration Release -p:PortableResultsAssetTargetFramework=netstandard2.0` - CI gate that runs the core test suite against the netstandard2.0 asset. -- `dotnet publish ./samples/NativeAotMovieRating/NativeAotMovieRating.csproj -c Release -r linux-x64` - CI gate for the Native AOT compatibility claim. Requires a platform linker (clang/gcc), see https://aka.ms/nativeaot-prerequisites. -- Benchmarks: `dotnet run -c Release --project benchmarks/Benchmarks -- --filter <glob>` (BenchmarkDotNet switcher). Run only for changes where performance is a material risk or requirement. -- Mutation testing: `dotnet tool restore`, then `UsePublicSigningKey=false dotnet stryker -p <project>` from the repository root (the environment variable opts out of public signing, which Stryker 4.16.0 cannot re-emit). Local, on-demand use only; see ./tests/AGENTS.md for configuration, baselines, and triage rules. +- `dotnet test Light.PortableResults.slnx` - builds the solution and runs all test projects. +- `dotnet test Light.PortableResults.slnx -- --coverage --coverage-settings "$PWD/coverage.runsettings" --coverage-output-format cobertura` - collects line coverage. Merge the reports with `reportgenerator -reports:'**/*.cobertura.xml' -targetdir:./coverage-merged -reporttypes:'Cobertura;TextSummary'`; the merged result must remain at or above 95%. +- `dotnet pack ./Light.PortableResults.slnx -c Release` - performs the Release build with warnings as errors and validates package APIs against the published baseline. This is the CI gate; see Package validation for intentional breaks and offline work. +- `dotnet test ./tests/Light.PortableResults.Tests/Light.PortableResults.Tests.csproj --configuration Release -p:PortableResultsAssetTargetFramework=netstandard2.0` - runs the core suite against the `netstandard2.0` asset. +- `dotnet publish ./samples/NativeAotMovieRating/NativeAotMovieRating.csproj -c Release -r linux-x64` - validates the Native AOT compatibility claim. It requires a platform linker such as clang or gcc. +- `dotnet run -c Release --project benchmarks/Benchmarks -- --filter <glob>` - runs selected benchmarks. Use it only when performance is a material risk or requirement. +- `dotnet tool restore`, then `UsePublicSigningKey=false dotnet stryker -p <project>` - runs mutation testing for a selected project. This is local and on demand; see `tests/mutation-testing.md` for configuration, baselines, triage, and known blind spots. -No dependency, secret, container, or source-code security scans are configured in this repository. +No dependency, secret, container, or source-code security scans are configured. -## General Rules for the Code Base +## Project conventions -In our Directory.Build.props files in this solution, the following rules are defined: +- Breaking changes are permitted before 1.0, but they must follow the package-validation and release-note requirements below. +- Prefer public APIs over internal ones when a type or member is properly encapsulated, so callers can configure and test the library without workarounds. See https://blog.ploeh.dk/2015/09/21/public-types-hidden-in-plain-sight/ +- Use Conventional Commits; add a body when the title does not adequately explain the rationale. -- The library is not published in a stable version yet, you can make breaking changes. -- When a type or method is properly encapsulated, make it public. We don't know how callers would like to use this library. When some types are internal, this might make it hard for callers to access these in tests or when making configuration changes. Prefer public APIs over internal ones. -- Use Conventional Commits messages. Decide whether a commit title is enough or a commit body is required. +## Package validation -## Package Validation +Every packable project under `src/` is validated against the version in `PackageValidationBaselineVersion` in `src/Directory.Build.props`. The package feedback loop above is authoritative; judge it by its exit code and run it before pushing an API change. -Every packable project under `src/` is validated against its published `0.7.0` package, so an undeclared change to the public API shape fails the build instead of reaching review as prose. The local gate is exactly the CI gate: +For an intentional break, describe it in `PackageReleaseNotes`, regenerate suppressions with `/p:ApiCompatGenerateSuppressionFile=true`, and review every generated entry. Unnecessary suppressions fail validation. ApiCompat checks assembly shape, not behavioral breaks such as changed encodings, guards, or exception types; cover those with tests and release notes. -```shell -dotnet pack ./Light.PortableResults.slnx -c Release -``` +When the baseline packages are unavailable, use `dotnet build ./Light.PortableResults.slnx -c Release -p:DisablePackageBaselineValidation=true`. This skips only the published-baseline comparison; never treat a build verified solely this way as final verification. -No extra arguments, properties or scripts. Run it before pushing an API change. - -Judge the result by the exit code, not by the console text: an incremental run may print package lines, build lines, or nothing at all, because the validation target skips when its inputs are unchanged. A non-zero exit and a `CP` diagnostic naming the affected API mean a break was found: - -- `CP0001`/`CP0002` - a type or member that `0.7.0` shipped is gone, or the `net10.0` asset is missing API that `netstandard2.0` has. -- `CP0003` - the assembly identity changed, in practice the strong-name key. -- `CP0011` - an enum field changed its numeric value. - -If the break is intentional, say so in `PackageReleaseNotes` and regenerate the suppression file with `/p:ApiCompatGenerateSuppressionFile=true`. Review every generated entry: the tool suppresses whatever it finds, including the break you did not mean to make. Unnecessary suppressions fail the build, so a suppression file never silently outlives the break it covers. - -ApiCompat compares assembly shape only. Behavioral breaks - encoding changes, new guard clauses, changed exception types - are invisible to it and stay the responsibility of tests and release notes. - -The baseline packages are acquired by a `PackageDownload` that is added during evaluation, so **every** restore fetches them, not just `pack`. A cold restore therefore pays one download of seven small packages; compilation and test execution afterwards are unaffected. `PackageDownload` does not write to `packages.lock.json`, which is why the CI cache key also hashes `src/Directory.Build.props`. - -To work offline, or whenever the baseline packages cannot be reached: - -```shell -dotnet build ./Light.PortableResults.slnx -c Release -p:DisablePackageBaselineValidation=true -``` - -The property works for `restore`, `build`, `test` and `pack`. It skips the baseline download and the baseline comparison, and it permits the then-unmatched baseline suppressions. Validation against the compatible frameworks inside the package stays active, so a `net10.0` asset that loses API relative to `netstandard2.0` still fails. Do not commit work verified only this way - the baseline comparison is the part that was switched off. - -Ordinary `src` builds are public-signed with the committed `Light.PortableResults.Public.snk` so that their identity matches the strong-named `0.7.0` baselines. This is set in `src/Directory.Build.props` rather than passed on the command line, precisely so that the plain `dotnet pack` above is the whole gate. Public signing carries the strong-name identity without a valid signature, which is why packages must only ever be pushed by the release workflow: it supplies the private key and produces genuinely signed assemblies. +Ordinary `src` builds are public-signed to match the published assembly identity. Only the release workflow supplies the private key, so never publish packages produced by an ordinary local or CI build. ### After a release -Once a version is published, raise `PackageValidationBaselineVersion` in `src/Directory.Build.props` to it, bump `Version` in the root `Directory.Build.props` to the next patch, empty every `PackageReleaseNotes`, and delete any `CompatibilitySuppressions.xml` under `src/` in the same commit. A suppression only ever covers a break against the previous baseline; against the new one it is unnecessary, and unnecessary suppressions fail the build. Each cycle therefore starts from a clean baseline with no suppression file. - -The `0.7.0` cleanup was done this way: the two intentional `MetadataKind.Array`/`MetadataKind.Object` renumberings from the `0.6.0` era stopped being breaks, so `src/Light.PortableResults/CompatibilitySuppressions.xml` was removed. - -## Testing Rules - -Read ./tests/AGENTS.md for details about how to write tests. +In one commit, set `PackageValidationBaselineVersion` to the released version, bump the root `Version` to the next patch, empty every `PackageReleaseNotes`, and delete all `CompatibilitySuppressions.xml` files under `src/`. ## How to write plans -Read ./ai-plans/AGENTS.md for details on how to write plans. +Follow `ai-plans/AGENTS.md` and use the corresponding Guided Coding skill before writing a plan or Plan Deviations document. ## This is your space -If you encounter something worth noting while you are working on this code base, write it down here in this section. Once you are finished, I will discuss it with you, and we can decide where to put your notes. +Found something noteworthy during implementation? Write down your discoveries here, we will discuss them afterward. diff --git a/src/Directory.Build.props b/src/Directory.Build.props index e4bc0e83..4038b6a3 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -46,8 +46,8 @@ the guard therefore reads that property, leaves UsePublicSigningKey empty and never sets PublicSign, so released packages stay privately signed. Do not guard on SignAssembly - the release sets that too. The guard also respects an externally set UsePublicSigningKey so that Stryker runs can opt out of signing: - Stryker 4.16.0 cannot re-emit mutated assemblies with a public-only key (CS7032), so tests/AGENTS.md - invokes it with UsePublicSigningKey=false in the environment. + Stryker 4.16.0 cannot re-emit mutated assemblies with a public-only key (CS7032), so the mutation-testing + command in the root AGENTS.md sets UsePublicSigningKey=false in the environment. --> <PropertyGroup> <UsePublicSigningKey Condition="'$(AssemblyOriginatorKeyFile)' == '' and '$(UsePublicSigningKey)' == ''">true</UsePublicSigningKey> diff --git a/tests/AGENTS.md b/tests/AGENTS.md index 0b060046..23de80ce 100644 --- a/tests/AGENTS.md +++ b/tests/AGENTS.md @@ -1,106 +1,20 @@ -# AGENTS.md for Tests +# Test instructions -## General Rules +## Test design -- Please do not use mocking frameworks like Moq or NSubstitute for test doubles, use hand-crafted Test Doubles instead. -- Do not write nested test classes. All tests should reside in a class which is directly placed in a namespace. -- Use PascalCase for test method names without underscores (e.g., `ParseThrowsForInvalidInput`). -- Use FluentAssertions instead of xunit's `Assert` class. -- When writing Unit Tests (i.e., tests that only run in-memory and make no I/O calls to third-party systems), prefer Sociable Tests instead of Solitary Tests (according to Martin Fowler's definition). Create as much test coverage as possible by calling higher level production APIs. Only write Solitary Tests to cover otherwise unreachable lower level APIs – for example, Guard Clauses. -- During Integration Tests, at least one I/O call to third-party systems like a database or Web API is made. Some of the third-party system calls can be replaced with Test Doubles or Fakes (according to XUnit Test Patterns by Gerard Meszaros). -- In End-to-End (E2E) Tests, I/O calls must not be replaced with Test Doubles or Fakes. -- Keep Code Coverage at least above 95%. Use Microsoft.Testing.Extensions.CodeCoverage to measure it. +- Use hand-written test doubles instead of mocking frameworks such as Moq or NSubstitute. +- Keep test classes at namespace level; nested helper and test-double types are allowed. +- Use FluentAssertions instead of xUnit's `Assert` class. +- Prefer sociable unit tests through the highest practical production API. Use solitary tests only for otherwise unreachable low-level contracts, such as guard clauses. -## How to run +## Mutation triage -- `dotnet test Light.PortableResults.slnx` for regular test runs. -- `dotnet test Light.PortableResults.slnx -- --coverage --coverage-settings "$PWD/coverage.runsettings" --coverage-output-format cobertura` for test coverage metrics. +Use the mutation-testing feedback loop documented in the root `AGENTS.md` and interpret results as follows: -Always pass `--coverage-settings`. It excludes source-generated files under `obj/`, which otherwise dominate the line counts and report the solution at roughly 81% instead of 95%. The path must be absolute, because each test app runs with its own output directory as the working directory. +1. If behavior is genuinely unconstrained, add or strengthen a contract-focused test. +2. If a mutant is equivalent or invalid, suppress it narrowly at the source with `// Stryker disable once Statement : equivalent - <reason>`. Do not use `disable all` or global `ignore-mutations` for a single site. +3. If a construct is untestable, record the limitation rather than manufacturing a test. -Each test project writes `TestResults/<guid>.cobertura.xml`. Merge them with `reportgenerator -reports:'**/*.cobertura.xml' -targetdir:./coverage-merged -reporttypes:'Cobertura;TextSummary'`. +Never restructure production code to improve mutation score; performance takes precedence. Tests added during triage must describe public behavior and must not mention Stryker, mutant IDs, source lines, incidental ordering, call counts, or other implementation details. -## Mutation testing (Stryker.NET) - -`dotnet tool restore` installs the pinned `dotnet-stryker` 4.16.0; the shared config is `stryker-config.json` at the repository root. Local, on-demand use only: no CI integration and no score gates (`thresholds`/`break-at` are unset). A surviving mutant is a signal to investigate, not proof of a defect. - -### How to run - -Always run from the repository root: Stryker reads `stryker-config.json` only from the current directory and silently falls back to defaults without it, reverting to the `vstest` runner (cannot drive the xUnit v3 hosts, reports everything survived) and `perTest` coverage analysis (fabricates `NoCoverage`). The config also pins `Debug` (`TreatWarningsAsErrors` is Release-only), `concurrency: 8` (result vectors vary with parallelism; override with `-c` for experiments), and `additional-timeout: 30000` (the 5,000 ms default masks slow survivors as timeouts under load). - -`additional-timeout` is config-file-only in 4.16.0 and is milliseconds of headroom added to Stryker's timeout derived from the initial test run, not the total timeout. Raising it makes genuine hangs take longer to classify, but the measured value exposes all known slow survivors while keeping the four small-project runs within minutes. - -Revisit `coverage-analysis: off` when Stryker's MTP per-test coverage support ([#3516](https://github.com/stryker-mutator/stryker-net/pull/3516)) ships and proves trustworthy; it is the main lever on run time because it avoids running the full discovered test set for every mutant. - -Always run Stryker with `UsePublicSigningKey=false` in the environment. Ordinary builds public-sign with the committed `Light.PortableResults.Public.snk` (see `src/Directory.Build.props`), and Stryker 4.16.0 cannot re-emit mutated assemblies with a public-only key — the mutation compile fails with `error CS7032` before any mutant is tested. The props file deliberately lets an externally set `UsePublicSigningKey` win over its default, so the environment variable opts the whole run (initial build, project analysis, and mutation compiles) out of signing. Stryker has no MSBuild-property passthrough, which is why this goes through the environment rather than a `-p:` argument. - -```shell -# One project — sufficient for the four small projects -UsePublicSigningKey=false dotnet stryker -p Light.PortableResults.AspNetCore.Shared.csproj - -# One-file configuration smoke check (~5 minutes) -# Expect: 2,675 tests, 67 killed, 0 NoCoverage, 100.00% -UsePublicSigningKey=false dotnet stryker -p Light.PortableResults.csproj -m '**/Result.cs' -``` - -The smoke-check vector is tied to the current `Result.cs` and its tests; update it after intentional changes alter the mutant inventory. All mutants surviving with a 0.00% score suggests fallback to the `vstest` runner, while any non-zero `NoCoverage` count suggests `coverage-analysis: off` was lost. - -`-p` selects the mutated project, not the tests: Stryker runs every test project transitively referencing it (`AspNetCore.Shared` → 415 tests, `Light.PortableResults` → all 2,675). Cross-project kills are legitimate (sociable tests) and nearly free. Never pass `-tp` (it does not narrow tests) or `--since` (any non-C# file in the diff, e.g. an `ai-plans/` document, degrades it to a full run). Reports go to `StrykerOutput/<timestamp>/reports/` (gitignored): JSON for agents (filter `"status"` for both `"Survived"` and `"Timeout"`; investigate the timeout cause before survivor triage), HTML for humans. - -### Cost and baseline - -~3 s per mutant at concurrency 8 (upper bound from the large projects). Omitting `-p` mutates the whole solution (~7 h; `Light.PortableResults` alone ~3.6 h) — split large projects by mutate glob along folder boundaries (`Metadata/`, `Http/`, `CloudEvents/`, `Numbers/`). Mutant inventory for sizing: - -| Project | Mutants | CompileError | -| --- | ---: | ---: | -| `Light.PortableResults` | 4,867 | 520 | -| `Light.PortableResults.Validation` | 2,215 | 83 | -| `Validation.OpenApi.SourceGeneration` | 1,272 | 204 | -| `AspNetCore.OpenApi` | 848 | 65 | -| `Validation.OpenApi` | 114 | 2 | -| `AspNetCore.Shared` | 47 | 3 | -| `AspNetCore.Mvc` | 33 | 11 | -| `AspNetCore.MinimalApis` | 32 | 11 | - -Baselines carry per-row provenance, because rows are re-measured individually as survivors are triaged. A row measured as part of the change that produced it cites the issue rather than a hash the commit cannot contain; find it with `git log --grep "Closes #<n>"`. All runs used `dotnet-stryker` 4.16.0, concurrency 8 and 30,000 ms additional timeout (both pinned in `stryker-config.json`), `Debug`, Apple M3 Max (16 logical cores): - -| Project | Provenance | Tests run | Elapsed | Killed | Timeout | Survived | CompileError | Ignored | NoCoverage | -| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | -| `AspNetCore.Mvc` | `#66` | 103 | 0:43 | 17 | 0 | 0 | 11 | 5 | 0 | -| `AspNetCore.MinimalApis` | `04aee20` | 237 | 1:22 | 16 | 0 | 0 | 11 | 5 | 0 | -| `AspNetCore.Shared` | `#80` | 415 | 2:44 | 34 | 0 | 0 | 3 | 10 | 0 | -| `Validation.OpenApi` | `#66` | 165 | 2:20 | 59 | 0 | 14 | 2 | 39 | 0 | - -Both `Validation.OpenApi` survivor groups are accounted for and neither is a missing test: twelve are the `target`-provided branch of the typed helpers, deferred to the bug in #57 so that tests are written against the corrected contract, and two are the known-false static-initializer mutants described in the blind spots below. Its 39 `Ignored` are 36 block-removal plus the three triage suppressions. - -At the 5,000 ms default additional timeout, `Validation.OpenApi` reported 21 mutants as timeouts, masking most of these survivors and inflating the score to 98.68%. At 20,000 ms, one of two runs still timed out a mutant known to survive. - -`Ignored` covers two distinct things, and the report carries the reason for each. Most entries are `Block removal` mutants discarded deterministically by Stryker's built-in "block already covered" filter because another active mutant exists inside the block. The rest are `Stryker disable once` suppressions from triage, which carry the justification written at the source. Check the reasons, not just the count: a `Block removal` count that moves without a source change should be investigated. - -`NoCoverage` must be zero — a non-zero value means `coverage-analysis: off` is no longer taking effect. Compare full count vectors at equal concurrency, not percentages. - -### Triaging survivors - -Exactly three permitted responses to a survivor: - -1. **Behavior is genuinely unconstrained** — add or strengthen a test (the common case). -2. **Equivalent or invalid mutant** — suppress narrowly at the source with a justification: `// Stryker disable once Statement : equivalent - <reason>`. Prefer `disable once` over `disable all`; never use the global `ignore-mutations` setting for a single site. -3. **Untestable construct** — record it and move on (blind spots below). - -Never restructure production code to make a mutant killable: performance outranks mutation score here, and the low-allocation `in`/`ref`/`Span` style produces awkward survivors by nature. - -The same discipline applies to test code. A test written under response 1 must stand on its own as a statement about the contract: name it for the behavior it pins down, and assert only on what the public API promises. Nothing in a test may refer to a mutant — no mutant ID, no line number, no mention of Stryker in a name, comment, or assertion message. The source suppression from response 2 is the only place a mutant is named. - -If a survivor can only be killed by asserting on something incidental — exact message composition, member or property ordering, a call count, a value the contract does not fix — it is response 2, not response 1. Suppress it with that reasoning. Such a test raises the score once and then constrains an implementation detail forever, which is a worse position than the survivor: the next legitimate refactoring breaks it, and the failure carries no information about the contract. - -### Blind spots — do not read as adequate coverage - -- ~9.5% of mutants fail to compile (mostly the `out`/`ref` style); Stryker's Safe Mode then discards every mutant in the enclosing method, which receives no mutation coverage at all. Tool limitation, not a test defect — a high score in `Numbers/` is not verified behavior. Observed at the baseline: `Dragon4.GenerateDigits`, `ResultJsonReader.ReadStatusValue`/`ReadIndexValue`, `ErrorsExtensions.WriteRichErrors`, and all of `LightResult.cs` (11 of 11 mutants). - - Treat that list as observed, not fixed: any new `out`/`ref` code joins it silently. Stryker announces it as `[INF] Safe Mode! Stryker will remove all mutations in <method>` on the console only — no log file is written — and the discarded mutants are simply absent from the JSON report. The durable way to recover the current set is to filter the report for `"status": "CompileError"`; those sites are the only trace left, and their enclosing methods are the ones running blind. - - When changing a method in that set, mutation score carries no information about it and line coverage only proves execution. Adequacy has to be argued by hand: enumerate the behaviors the method promises and point at the test constraining each one. State that reasoning in the pull request, because no tool in this repository can check it. -- Mutants reachable only during static initialization are reported as survived even when the suite kills them. Measured on `BuiltInValidationErrorContracts`: emptying the `Contracts` registry fails 46 of 112 tests and blanking the built-in schema id string fails 52 of 112, yet Stryker reported both as `Survived`. Both sites run only while a static property initializer executes, which a reused test host runs once per process, independently of mutant activation. Verify any survivor in a static initializer, a static constructor, or a helper called only from one by applying the mutation to the source by hand and running the affected test project — the report cannot settle it. Do not add tests for such a survivor before that check: the two above already had covering assertions. -- `MetadataValueReconstructor` keeps `OperationCanceledException` out of its two evaluation catch filters, and that contract cannot be observed through the generator's public surface: Roslyn intercepts a pre-cancelled token before reconstruction runs, and none of the whitelisted framework constructors and factories can throw cancellation. No test can distinguish the filter from a plain `catch (Exception)`, so the contract rests on the filters' structure. It becomes testable — and needs a test — as soon as an accepted evaluation gains a reachable cancellation path. -- `Timeout` counts as killed. The pinned 30,000 ms additional timeout reduced `Validation.OpenApi` from 21 timeouts to zero in two consecutive concurrency-8 runs, but no finite value makes classification independent of hardware and load. Investigate any future timeout as either a genuine hang or insufficient headroom; do not assume it represents a killed mutant. -- The MTP runner is a preview (stryker-mutator/stryker-net#3094); verify surprising results against a plain `dotnet test` run. +Investigate every timeout. Treat compile errors and Safe Mode as mutation-coverage gaps, and verify static-initializer survivors manually before adding tests. See `mutation-testing.md` for the operating guide and known blind spots. diff --git a/tests/mutation-testing.md b/tests/mutation-testing.md new file mode 100644 index 00000000..19ce4ca7 --- /dev/null +++ b/tests/mutation-testing.md @@ -0,0 +1,51 @@ +# Mutation testing with Stryker.NET + +The root `AGENTS.md` is the single source for feedback-loop commands. This document explains the Stryker configuration, report interpretation, baselines, and known limitations. + +## Configuration and reports + +`dotnet tool restore` installs the pinned Stryker.NET 4.16.0. Mutation testing is local and on demand, with no CI or score gate. Always run from the repository root: otherwise Stryker misses `stryker-config.json`, falls back to the `vstest` runner and per-test coverage analysis, and can report all mutants as survived or fabricate `NoCoverage` results. + +The shared configuration selects the MTP runner, disables unreliable per-test coverage analysis, uses Debug because warnings are errors only in Release, pins concurrency at 8, and adds 30,000 ms of timeout headroom. `additional-timeout` is config-file-only in 4.16.0 and is added to the duration derived from the initial test run; it is not the total timeout. Revisit `coverage-analysis: off` when Stryker's MTP per-test coverage support ([#3516](https://github.com/stryker-mutator/stryker-net/pull/3516)) ships and proves reliable. + +Mutation runs disable public signing through the environment because Stryker 4.16.0 cannot re-emit assemblies using the committed public-only key (`CS7032`). `src/Directory.Build.props` deliberately lets an externally supplied `UsePublicSigningKey` value override its default. + +`-p` selects the mutated project, not a test project. Stryker runs every test project that transitively references it, so cross-project kills are legitimate. Do not pass `-tp`, which does not narrow this test set, or `--since`, because a non-C# file in the diff degrades it to a full run. + +Reports are written to `StrykerOutput/<timestamp>/reports/`: JSON is intended for automated inspection and HTML for humans. In JSON, inspect both `Survived` and `Timeout` statuses. `NoCoverage` must remain zero; a non-zero count means `coverage-analysis: off` was not applied. Compare complete count vectors at equal concurrency rather than percentages. + +For a one-file configuration smoke check, mutate `**/Result.cs` in `Light.PortableResults.csproj`. The current expected vector is 2,675 tests, 67 killed, zero `NoCoverage`, and a 100% score. Update this vector after intentional changes to `Result.cs` or its tests. A 0% score for every mutant usually indicates fallback to `vstest`. + +## Cost and measured baselines + +Budget approximately three seconds per mutant at concurrency 8 for the large projects. Running without `-p` mutates the whole solution and has taken about seven hours; `Light.PortableResults` alone has taken about 3.6 hours. Split large projects by folder-oriented mutate globs such as `Metadata/`, `Http/`, `CloudEvents/`, and `Numbers/`. + +| Project | Mutants | CompileError | +| --- | ---: | ---: | +| `Light.PortableResults` | 4,867 | 520 | +| `Light.PortableResults.Validation` | 2,215 | 83 | +| `Validation.OpenApi.SourceGeneration` | 1,272 | 204 | +| `AspNetCore.OpenApi` | 848 | 65 | +| `Validation.OpenApi` | 114 | 2 | +| `AspNetCore.Shared` | 47 | 3 | +| `AspNetCore.Mvc` | 33 | 11 | +| `AspNetCore.MinimalApis` | 32 | 11 | + +The following baselines were measured with Stryker.NET 4.16.0, Debug, concurrency 8, 30,000 ms additional timeout, and an Apple M3 Max with 16 logical cores. Issue provenance refers to the change that produced the row; find it with `git log --grep "Closes #<n>"`. + +| Project | Provenance | Tests | Elapsed | Killed | Timeout | Survived | CompileError | Ignored | NoCoverage | +| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | +| `AspNetCore.Mvc` | `#66` | 103 | 0:43 | 17 | 0 | 0 | 11 | 5 | 0 | +| `AspNetCore.MinimalApis` | `04aee20` | 237 | 1:22 | 16 | 0 | 0 | 11 | 5 | 0 | +| `AspNetCore.Shared` | `#80` | 415 | 2:44 | 34 | 0 | 0 | 3 | 10 | 0 | +| `Validation.OpenApi` | `#66` | 165 | 2:20 | 59 | 0 | 14 | 2 | 39 | 0 | + +The fourteen `Validation.OpenApi` survivors are accounted for: twelve exercise the `target`-provided branch of typed helpers deferred to issue #57, and two are false static-initializer results described below. Its remaining ignored mutants are Stryker's deterministic block-removal filter or narrow source suppressions. Inspect ignore reasons rather than relying only on the count. + +## Known blind spots + +- **Compile errors and Safe Mode:** About 9.5% of the baseline mutants fail to compile, mostly around `out` and `ref`. Safe Mode then removes all mutants in the enclosing method. Observed blind spots include `Dragon4.GenerateDigits`, `ResultJsonReader.ReadStatusValue` and `ReadIndexValue`, `ErrorsExtensions.WriteRichErrors`, and all of `LightResult.cs`. Recover current sites from JSON entries with `status: CompileError`; when changing one, manually map promised behaviors to tests and explain that adequacy in the pull request. +- **Static initialization:** Mutants reached only by static initializers or constructors can appear to survive because the reused test host initializes the type before mutant activation. Verify them by applying the mutation to source and running the affected test project before adding tests. This behavior was confirmed for the `BuiltInValidationErrorContracts` registry and schema ID. +- **Unreachable cancellation:** `MetadataValueReconstructor` excludes `OperationCanceledException` in two catch filters, but Roslyn intercepts pre-cancelled tokens before reconstruction and the accepted framework evaluations cannot throw cancellation. The distinction becomes testable only if an accepted evaluation gains a reachable cancellation path. +- **Timeout classification:** Stryker counts timeouts as killed, but classification depends on hardware and load. The configured headroom eliminated known false timeouts in repeated baseline runs; investigate every future timeout as a possible hang or insufficient headroom. +- **Preview runner:** The MTP runner remains a preview integration ([#3094](https://github.com/stryker-mutator/stryker-net/issues/3094)). Verify surprising results with a plain `dotnet test` run. From 3280a08c9d09b80ff2d66398027282ed6bb31069 Mon Sep 17 00:00:00 2001 From: Kenny Pflug <kenny.pflug@live.de> Date: Fri, 14 Aug 2026 16:15:29 +0200 Subject: [PATCH 3/3] chore: update packages.lock.json files, remove the one from the NativeAotMovieRating sample Signed-off-by: Kenny Pflug <kenny.pflug@live.de> --- benchmarks/Benchmarks/packages.lock.json | 8 +- global.json | 2 +- .../NativeAotMovieRating.csproj | 2 + .../NativeAotMovieRating/packages.lock.json | 210 ------------------ .../packages.lock.json | 8 +- .../packages.lock.json | 2 +- .../packages.lock.json | 8 +- .../packages.lock.json | 6 +- .../packages.lock.json | 12 +- .../packages.lock.json | 6 +- src/Light.PortableResults/packages.lock.json | 6 +- .../packages.lock.json | 4 +- .../packages.lock.json | 4 +- .../packages.lock.json | 8 +- .../packages.lock.json | 2 +- .../packages.lock.json | 10 +- .../packages.lock.json | 12 +- .../packages.lock.json | 2 +- 18 files changed, 52 insertions(+), 260 deletions(-) delete mode 100644 samples/NativeAotMovieRating/packages.lock.json diff --git a/benchmarks/Benchmarks/packages.lock.json b/benchmarks/Benchmarks/packages.lock.json index 3823fff5..1f73936a 100644 --- a/benchmarks/Benchmarks/packages.lock.json +++ b/benchmarks/Benchmarks/packages.lock.json @@ -122,25 +122,25 @@ "light.portableresults.aspnetcore.minimalapis": { "type": "Project", "dependencies": { - "Light.PortableResults.AspNetCore.Shared": "[0.6.0, )" + "Light.PortableResults.AspNetCore.Shared": "[0.7.1, )" } }, "light.portableresults.aspnetcore.mvc": { "type": "Project", "dependencies": { - "Light.PortableResults.AspNetCore.Shared": "[0.6.0, )" + "Light.PortableResults.AspNetCore.Shared": "[0.7.1, )" } }, "light.portableresults.aspnetcore.shared": { "type": "Project", "dependencies": { - "Light.PortableResults": "[0.6.0, )" + "Light.PortableResults": "[0.7.1, )" } }, "light.portableresults.validation": { "type": "Project", "dependencies": { - "Light.PortableResults": "[0.6.0, )" + "Light.PortableResults": "[0.7.1, )" } }, "Microsoft.Bcl.HashCode": { diff --git a/global.json b/global.json index 7325398c..a0703e84 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "10.0.302", + "version": "10.0.400", "rollForward": "latestFeature" }, "test": { diff --git a/samples/NativeAotMovieRating/NativeAotMovieRating.csproj b/samples/NativeAotMovieRating/NativeAotMovieRating.csproj index 781cbba1..817e4d07 100644 --- a/samples/NativeAotMovieRating/NativeAotMovieRating.csproj +++ b/samples/NativeAotMovieRating/NativeAotMovieRating.csproj @@ -15,6 +15,8 @@ and globally suppressing IL2067 could hide the same defect in the Light assemblies. --> <WarningsNotAsErrors>$(WarningsNotAsErrors);IL2104</WarningsNotAsErrors> + <!-- For this sample project, we do not use packages.lock.json files as they differ on different OSes --> + <RestorePackagesWithLockFile>false</RestorePackagesWithLockFile> <RestoreLockedMode>false</RestoreLockedMode> </PropertyGroup> diff --git a/samples/NativeAotMovieRating/packages.lock.json b/samples/NativeAotMovieRating/packages.lock.json deleted file mode 100644 index e5490397..00000000 --- a/samples/NativeAotMovieRating/packages.lock.json +++ /dev/null @@ -1,210 +0,0 @@ -{ - "version": 2, - "dependencies": { - "net10.0": { - "Light.SharedCore": { - "type": "Direct", - "requested": "[3.0.0, )", - "resolved": "3.0.0", - "contentHash": "uxMjietKkIJdZW1yU8gbE4FDcbvjqyvAd/DwxW2Zapu4CnFm7rNmMJdRhULdqs6+M+J2vmMBPZ+8pIQB6Km2Ag==", - "dependencies": { - "Light.GuardClauses": "13.0.0" - } - }, - "Microsoft.AspNetCore.OpenApi": { - "type": "Direct", - "requested": "[10.0.10, )", - "resolved": "10.0.10", - "contentHash": "d4Atx9IHq7JgX0F/h7Db+m9zAUzC+cKdI9k+OWnnyQIOUQtfvjIEuhvbjPigVMkAmPUgCbJ8Yp6M9ghUqHtJSQ==", - "dependencies": { - "Microsoft.OpenApi": "2.0.0" - } - }, - "Microsoft.DotNet.ILCompiler": { - "type": "Direct", - "requested": "[10.0.10, )", - "resolved": "10.0.10", - "contentHash": "tnG8ntt/Bk6odvHREnGLMo3PEiihy5iSlIFVp0JbIo00GKtNRt2k73eKZbPqR5yaJNIa3z8R86YLwbxfqpb17g==" - }, - "Microsoft.NET.ILLink.Tasks": { - "type": "Direct", - "requested": "[10.0.10, )", - "resolved": "10.0.10", - "contentHash": "f5VCIE7AJpd5YvzNTeMGVzQIgyE9tX+AreTYwQF+REbu+DZo/2Ae+jNSwhPEYrVz6RRkd7y8ubXjk6Nn6Ka+Cg==" - }, - "Scalar.AspNetCore": { - "type": "Direct", - "requested": "[2.16.16, )", - "resolved": "2.16.16", - "contentHash": "Ax0e0bIh+Upf92k1+pTBUom3e/kbpu20qsrDYmmS1NM721Eq2xF8c789x6IbiNrvW8WwySRzPv/icYYhb6idSg==" - }, - "Serilog.AspNetCore": { - "type": "Direct", - "requested": "[10.0.0, )", - "resolved": "10.0.0", - "contentHash": "a/cNa1mY4On1oJlfGG1wAvxjp5g7OEzk/Jf/nm7NF9cWoE7KlZw1GldrifUBWm9oKibHkR7Lg/l5jy3y7ACR8w==", - "dependencies": { - "Serilog": "4.3.0", - "Serilog.Extensions.Hosting": "10.0.0", - "Serilog.Formatting.Compact": "3.0.0", - "Serilog.Settings.Configuration": "10.0.0", - "Serilog.Sinks.Console": "6.1.1", - "Serilog.Sinks.Debug": "3.0.0", - "Serilog.Sinks.File": "7.0.0" - } - }, - "Swashbuckle.AspNetCore.SwaggerUI": { - "type": "Direct", - "requested": "[10.2.3, )", - "resolved": "10.2.3", - "contentHash": "nthWONRs/FJ4yyG206g1cC52WEG8EqrjuMWjGdR+5XG7lbjFto6NqcI9EMICgVFom/UivIjUVwI76ZHbHwTPfQ==" - }, - "Light.GuardClauses": { - "type": "Transitive", - "resolved": "13.0.0", - "contentHash": "mhlWUk0o+XibJgYuPlO9vQQpMmh4EAeDRFAac7pP6W3zmpjEeyUVGOiw+mdCxX469QQ2rkMYjWyHoDlZv599/w==" - }, - "Microsoft.Extensions.DependencyModel": { - "type": "Transitive", - "resolved": "10.0.0", - "contentHash": "RFYJR7APio/BiqdQunRq6DB+nDB6nc2qhHr77mlvZ0q0BT8PubMXN7XicmfzCbrDE/dzhBnUKBRXLTcqUiZDGg==" - }, - "Serilog": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "+cDryFR0GRhsGOnZSKwaDzRRl4MupvJ42FhCE4zhQRVanX0Jpg6WuCBk59OVhVDPmab1bB+nRykAnykYELA9qQ==" - }, - "Serilog.Extensions.Hosting": { - "type": "Transitive", - "resolved": "10.0.0", - "contentHash": "E7juuIc+gzoGxgzFooFgAV8g9BfiSXNKsUok9NmEpyAXg2odkcPsMa/Yo4axkJRlh0se7mkYQ1GXDaBemR+b6w==", - "dependencies": { - "Serilog": "4.3.0", - "Serilog.Extensions.Logging": "10.0.0" - } - }, - "Serilog.Extensions.Logging": { - "type": "Transitive", - "resolved": "10.0.0", - "contentHash": "vx0kABKl2dWbBhhqAfTOk53/i8aV/5VaT3a6il9gn72Wqs2pM7EK2OB6No6xdqK2IaY6Zf9gdjLuK9BVa2rT+Q==", - "dependencies": { - "Serilog": "4.2.0" - } - }, - "Serilog.Formatting.Compact": { - "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "wQsv14w9cqlfB5FX2MZpNsTawckN4a8dryuNGbebB/3Nh1pXnROHZov3swtu3Nj5oNG7Ba+xdu7Et/ulAUPanQ==", - "dependencies": { - "Serilog": "4.0.0" - } - }, - "Serilog.Settings.Configuration": { - "type": "Transitive", - "resolved": "10.0.0", - "contentHash": "LNq+ibS1sbhTqPV1FIE69/9AJJbfaOhnaqkzcjFy95o+4U+STsta9mi97f1smgXsWYKICDeGUf8xUGzd/52/uA==", - "dependencies": { - "Microsoft.Extensions.DependencyModel": "10.0.0", - "Serilog": "4.3.0" - } - }, - "Serilog.Sinks.Console": { - "type": "Transitive", - "resolved": "6.1.1", - "contentHash": "8jbqgjUyZlfCuSTaJk6lOca465OndqOz3KZP6Cryt/IqZYybyBu7GP0fE/AXBzrrQB3EBmQntBFAvMVz1COvAA==", - "dependencies": { - "Serilog": "4.0.0" - } - }, - "Serilog.Sinks.Debug": { - "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "4BzXcdrgRX7wde9PmHuYd9U6YqycCC28hhpKonK7hx0wb19eiuRj16fPcPSVp0o/Y1ipJuNLYQ00R3q2Zs8FDA==", - "dependencies": { - "Serilog": "4.0.0" - } - }, - "Serilog.Sinks.File": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "fKL7mXv7qaiNBUC71ssvn/dU0k9t0o45+qm2XgKAlSt19xF+ijjxyA3R6HmCgfKEKwfcfkwWjayuQtRueZFkYw==", - "dependencies": { - "Serilog": "4.2.0" - } - }, - "light.portableresults": { - "type": "Project", - "dependencies": { - "Microsoft.Bcl.HashCode": "[6.0.0, )", - "Ulid": "[1.4.1, )" - } - }, - "light.portableresults.aspnetcore.minimalapis": { - "type": "Project", - "dependencies": { - "Light.PortableResults.AspNetCore.Shared": "[0.7.0, )" - } - }, - "light.portableresults.aspnetcore.openapi": { - "type": "Project", - "dependencies": { - "Light.PortableResults.AspNetCore.Shared": "[0.7.0, )", - "Microsoft.AspNetCore.OpenApi": "[10.0.10, )" - } - }, - "light.portableresults.aspnetcore.shared": { - "type": "Project", - "dependencies": { - "Light.PortableResults": "[0.7.0, )" - } - }, - "light.portableresults.validation": { - "type": "Project", - "dependencies": { - "Light.PortableResults": "[0.7.0, )" - } - }, - "light.portableresults.validation.openapi": { - "type": "Project", - "dependencies": { - "Light.PortableResults.AspNetCore.OpenApi": "[0.7.0, )", - "Light.PortableResults.Validation": "[0.7.0, )" - } - }, - "Microsoft.Bcl.HashCode": { - "type": "CentralTransitive", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "GI4jcoi6eC9ZhNOQylIBaWOQjyGaR8T6N3tC1u8p3EXfndLCVNNWa+Zp+ocjvvS3kNBN09Zma2HXL0ezO0dRfw==" - }, - "Microsoft.OpenApi": { - "type": "CentralTransitive", - "requested": "[2.11.0, )", - "resolved": "2.11.0", - "contentHash": "/ignjfdeKT2SGLIR7QEv19KnI0rvoxRG/TYDOZdK9EsWLjKK9IK8i1Mo5NRm9PRV3i64DzlTqnIflWvoyfljLg==" - }, - "Ulid": { - "type": "CentralTransitive", - "requested": "[1.4.1, )", - "resolved": "1.4.1", - "contentHash": "V6crLJ8a29raWeNwxYGfH9RTKA3H0nR0D9LAGzN3KtEsbiiaWkUjDor6OT5Oz7pxCK+NaY2hu2FLoYEOa8oCkA==" - } - }, - "net10.0/osx-arm64": { - "Microsoft.DotNet.ILCompiler": { - "type": "Direct", - "requested": "[10.0.10, )", - "resolved": "10.0.10", - "contentHash": "tnG8ntt/Bk6odvHREnGLMo3PEiihy5iSlIFVp0JbIo00GKtNRt2k73eKZbPqR5yaJNIa3z8R86YLwbxfqpb17g==", - "dependencies": { - "runtime.osx-arm64.Microsoft.DotNet.ILCompiler": "10.0.10" - } - }, - "runtime.osx-arm64.Microsoft.DotNet.ILCompiler": { - "type": "Transitive", - "resolved": "10.0.10", - "contentHash": "cY7edFqVviQMiSvPodJeLZhF6k56grh0QsvcR+2foAUgO0xXsMsVBQiHcZkeiIbhr+SPIklq90OZiJ7JVrc+Dg==" - } - } - } -} \ No newline at end of file diff --git a/src/Light.PortableResults.AspNetCore.MinimalApis/packages.lock.json b/src/Light.PortableResults.AspNetCore.MinimalApis/packages.lock.json index 05fbdb16..1a7caaa9 100644 --- a/src/Light.PortableResults.AspNetCore.MinimalApis/packages.lock.json +++ b/src/Light.PortableResults.AspNetCore.MinimalApis/packages.lock.json @@ -4,9 +4,9 @@ "net10.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[10.0.10, )", - "resolved": "10.0.10", - "contentHash": "f5VCIE7AJpd5YvzNTeMGVzQIgyE9tX+AreTYwQF+REbu+DZo/2Ae+jNSwhPEYrVz6RRkd7y8ubXjk6Nn6Ka+Cg==" + "requested": "[10.0.11, )", + "resolved": "10.0.11", + "contentHash": "IBf7lbovvjGWVWXZX5cJ/cO0WXbId0Zq4BuSeT94mGZuOAP66oMeH9PTBZ9Jpp3Jb6jtK0qm/NyUbPRo1gC/wQ==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", @@ -47,7 +47,7 @@ "light.portableresults.aspnetcore.shared": { "type": "Project", "dependencies": { - "Light.PortableResults": "[0.7.0, )" + "Light.PortableResults": "[0.7.1, )" } }, "Microsoft.Bcl.HashCode": { diff --git a/src/Light.PortableResults.AspNetCore.Mvc/packages.lock.json b/src/Light.PortableResults.AspNetCore.Mvc/packages.lock.json index 07972552..907f570a 100644 --- a/src/Light.PortableResults.AspNetCore.Mvc/packages.lock.json +++ b/src/Light.PortableResults.AspNetCore.Mvc/packages.lock.json @@ -41,7 +41,7 @@ "light.portableresults.aspnetcore.shared": { "type": "Project", "dependencies": { - "Light.PortableResults": "[0.6.0, )" + "Light.PortableResults": "[0.7.1, )" } }, "Microsoft.Bcl.HashCode": { diff --git a/src/Light.PortableResults.AspNetCore.OpenApi/packages.lock.json b/src/Light.PortableResults.AspNetCore.OpenApi/packages.lock.json index 7a499d88..0185eca6 100644 --- a/src/Light.PortableResults.AspNetCore.OpenApi/packages.lock.json +++ b/src/Light.PortableResults.AspNetCore.OpenApi/packages.lock.json @@ -13,9 +13,9 @@ }, "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[10.0.10, )", - "resolved": "10.0.10", - "contentHash": "f5VCIE7AJpd5YvzNTeMGVzQIgyE9tX+AreTYwQF+REbu+DZo/2Ae+jNSwhPEYrVz6RRkd7y8ubXjk6Nn6Ka+Cg==" + "requested": "[10.0.11, )", + "resolved": "10.0.11", + "contentHash": "IBf7lbovvjGWVWXZX5cJ/cO0WXbId0Zq4BuSeT94mGZuOAP66oMeH9PTBZ9Jpp3Jb6jtK0qm/NyUbPRo1gC/wQ==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", @@ -56,7 +56,7 @@ "light.portableresults.aspnetcore.shared": { "type": "Project", "dependencies": { - "Light.PortableResults": "[0.7.0, )" + "Light.PortableResults": "[0.7.1, )" } }, "Microsoft.Bcl.HashCode": { diff --git a/src/Light.PortableResults.AspNetCore.Shared/packages.lock.json b/src/Light.PortableResults.AspNetCore.Shared/packages.lock.json index 8ed70a20..33152964 100644 --- a/src/Light.PortableResults.AspNetCore.Shared/packages.lock.json +++ b/src/Light.PortableResults.AspNetCore.Shared/packages.lock.json @@ -4,9 +4,9 @@ "net10.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[10.0.10, )", - "resolved": "10.0.10", - "contentHash": "f5VCIE7AJpd5YvzNTeMGVzQIgyE9tX+AreTYwQF+REbu+DZo/2Ae+jNSwhPEYrVz6RRkd7y8ubXjk6Nn6Ka+Cg==" + "requested": "[10.0.11, )", + "resolved": "10.0.11", + "contentHash": "IBf7lbovvjGWVWXZX5cJ/cO0WXbId0Zq4BuSeT94mGZuOAP66oMeH9PTBZ9Jpp3Jb6jtK0qm/NyUbPRo1gC/wQ==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", diff --git a/src/Light.PortableResults.Validation.OpenApi/packages.lock.json b/src/Light.PortableResults.Validation.OpenApi/packages.lock.json index d9639984..22df860c 100644 --- a/src/Light.PortableResults.Validation.OpenApi/packages.lock.json +++ b/src/Light.PortableResults.Validation.OpenApi/packages.lock.json @@ -4,9 +4,9 @@ "net10.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[10.0.10, )", - "resolved": "10.0.10", - "contentHash": "f5VCIE7AJpd5YvzNTeMGVzQIgyE9tX+AreTYwQF+REbu+DZo/2Ae+jNSwhPEYrVz6RRkd7y8ubXjk6Nn6Ka+Cg==" + "requested": "[10.0.11, )", + "resolved": "10.0.11", + "contentHash": "IBf7lbovvjGWVWXZX5cJ/cO0WXbId0Zq4BuSeT94mGZuOAP66oMeH9PTBZ9Jpp3Jb6jtK0qm/NyUbPRo1gC/wQ==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", @@ -54,20 +54,20 @@ "light.portableresults.aspnetcore.openapi": { "type": "Project", "dependencies": { - "Light.PortableResults.AspNetCore.Shared": "[0.7.0, )", + "Light.PortableResults.AspNetCore.Shared": "[0.7.1, )", "Microsoft.AspNetCore.OpenApi": "[10.0.10, )" } }, "light.portableresults.aspnetcore.shared": { "type": "Project", "dependencies": { - "Light.PortableResults": "[0.7.0, )" + "Light.PortableResults": "[0.7.1, )" } }, "light.portableresults.validation": { "type": "Project", "dependencies": { - "Light.PortableResults": "[0.7.0, )" + "Light.PortableResults": "[0.7.1, )" } }, "Microsoft.AspNetCore.OpenApi": { diff --git a/src/Light.PortableResults.Validation/packages.lock.json b/src/Light.PortableResults.Validation/packages.lock.json index 8f5b6b4c..f00a764b 100644 --- a/src/Light.PortableResults.Validation/packages.lock.json +++ b/src/Light.PortableResults.Validation/packages.lock.json @@ -208,9 +208,9 @@ "net10.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[10.0.10, )", - "resolved": "10.0.10", - "contentHash": "f5VCIE7AJpd5YvzNTeMGVzQIgyE9tX+AreTYwQF+REbu+DZo/2Ae+jNSwhPEYrVz6RRkd7y8ubXjk6Nn6Ka+Cg==" + "requested": "[10.0.11, )", + "resolved": "10.0.11", + "contentHash": "IBf7lbovvjGWVWXZX5cJ/cO0WXbId0Zq4BuSeT94mGZuOAP66oMeH9PTBZ9Jpp3Jb6jtK0qm/NyUbPRo1gC/wQ==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", diff --git a/src/Light.PortableResults/packages.lock.json b/src/Light.PortableResults/packages.lock.json index ba5d68fc..adceca3d 100644 --- a/src/Light.PortableResults/packages.lock.json +++ b/src/Light.PortableResults/packages.lock.json @@ -219,9 +219,9 @@ }, "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[10.0.10, )", - "resolved": "10.0.10", - "contentHash": "f5VCIE7AJpd5YvzNTeMGVzQIgyE9tX+AreTYwQF+REbu+DZo/2Ae+jNSwhPEYrVz6RRkd7y8ubXjk6Nn6Ka+Cg==" + "requested": "[10.0.11, )", + "resolved": "10.0.11", + "contentHash": "IBf7lbovvjGWVWXZX5cJ/cO0WXbId0Zq4BuSeT94mGZuOAP66oMeH9PTBZ9Jpp3Jb6jtK0qm/NyUbPRo1gC/wQ==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", diff --git a/tests/Light.PortableResults.AspNetCore.MinimalApis.Tests/packages.lock.json b/tests/Light.PortableResults.AspNetCore.MinimalApis.Tests/packages.lock.json index 8084245e..0ebc8473 100644 --- a/tests/Light.PortableResults.AspNetCore.MinimalApis.Tests/packages.lock.json +++ b/tests/Light.PortableResults.AspNetCore.MinimalApis.Tests/packages.lock.json @@ -602,13 +602,13 @@ "light.portableresults.aspnetcore.minimalapis": { "type": "Project", "dependencies": { - "Light.PortableResults.AspNetCore.Shared": "[0.6.0, )" + "Light.PortableResults.AspNetCore.Shared": "[0.7.1, )" } }, "light.portableresults.aspnetcore.shared": { "type": "Project", "dependencies": { - "Light.PortableResults": "[0.6.0, )" + "Light.PortableResults": "[0.7.1, )" } }, "Microsoft.Bcl.AsyncInterfaces": { diff --git a/tests/Light.PortableResults.AspNetCore.Mvc.Tests/packages.lock.json b/tests/Light.PortableResults.AspNetCore.Mvc.Tests/packages.lock.json index db898cef..53cd5e6e 100644 --- a/tests/Light.PortableResults.AspNetCore.Mvc.Tests/packages.lock.json +++ b/tests/Light.PortableResults.AspNetCore.Mvc.Tests/packages.lock.json @@ -602,13 +602,13 @@ "light.portableresults.aspnetcore.mvc": { "type": "Project", "dependencies": { - "Light.PortableResults.AspNetCore.Shared": "[0.6.0, )" + "Light.PortableResults.AspNetCore.Shared": "[0.7.1, )" } }, "light.portableresults.aspnetcore.shared": { "type": "Project", "dependencies": { - "Light.PortableResults": "[0.6.0, )" + "Light.PortableResults": "[0.7.1, )" } }, "Microsoft.Bcl.AsyncInterfaces": { diff --git a/tests/Light.PortableResults.AspNetCore.OpenApi.Tests/packages.lock.json b/tests/Light.PortableResults.AspNetCore.OpenApi.Tests/packages.lock.json index de4f56d2..f4831514 100644 --- a/tests/Light.PortableResults.AspNetCore.OpenApi.Tests/packages.lock.json +++ b/tests/Light.PortableResults.AspNetCore.OpenApi.Tests/packages.lock.json @@ -243,26 +243,26 @@ "light.portableresults.aspnetcore.minimalapis": { "type": "Project", "dependencies": { - "Light.PortableResults.AspNetCore.Shared": "[0.6.0, )" + "Light.PortableResults.AspNetCore.Shared": "[0.7.1, )" } }, "light.portableresults.aspnetcore.mvc": { "type": "Project", "dependencies": { - "Light.PortableResults.AspNetCore.Shared": "[0.6.0, )" + "Light.PortableResults.AspNetCore.Shared": "[0.7.1, )" } }, "light.portableresults.aspnetcore.openapi": { "type": "Project", "dependencies": { - "Light.PortableResults.AspNetCore.Shared": "[0.6.0, )", + "Light.PortableResults.AspNetCore.Shared": "[0.7.1, )", "Microsoft.AspNetCore.OpenApi": "[10.0.10, )" } }, "light.portableresults.aspnetcore.shared": { "type": "Project", "dependencies": { - "Light.PortableResults": "[0.6.0, )" + "Light.PortableResults": "[0.7.1, )" } }, "Microsoft.AspNetCore.OpenApi": { diff --git a/tests/Light.PortableResults.AspNetCore.Shared.Tests/packages.lock.json b/tests/Light.PortableResults.AspNetCore.Shared.Tests/packages.lock.json index 4f477dc2..0655c5d3 100644 --- a/tests/Light.PortableResults.AspNetCore.Shared.Tests/packages.lock.json +++ b/tests/Light.PortableResults.AspNetCore.Shared.Tests/packages.lock.json @@ -235,7 +235,7 @@ "light.portableresults.aspnetcore.shared": { "type": "Project", "dependencies": { - "Light.PortableResults": "[0.6.0, )" + "Light.PortableResults": "[0.7.1, )" } }, "Microsoft.Bcl.AsyncInterfaces": { diff --git a/tests/Light.PortableResults.Validation.OpenApi.SourceGeneration.Tests/packages.lock.json b/tests/Light.PortableResults.Validation.OpenApi.SourceGeneration.Tests/packages.lock.json index c3aaabb8..7475237e 100644 --- a/tests/Light.PortableResults.Validation.OpenApi.SourceGeneration.Tests/packages.lock.json +++ b/tests/Light.PortableResults.Validation.OpenApi.SourceGeneration.Tests/packages.lock.json @@ -253,27 +253,27 @@ "light.portableresults.aspnetcore.openapi": { "type": "Project", "dependencies": { - "Light.PortableResults.AspNetCore.Shared": "[0.6.0, )", + "Light.PortableResults.AspNetCore.Shared": "[0.7.1, )", "Microsoft.AspNetCore.OpenApi": "[10.0.10, )" } }, "light.portableresults.aspnetcore.shared": { "type": "Project", "dependencies": { - "Light.PortableResults": "[0.6.0, )" + "Light.PortableResults": "[0.7.1, )" } }, "light.portableresults.validation": { "type": "Project", "dependencies": { - "Light.PortableResults": "[0.6.0, )" + "Light.PortableResults": "[0.7.1, )" } }, "light.portableresults.validation.openapi": { "type": "Project", "dependencies": { - "Light.PortableResults.AspNetCore.OpenApi": "[0.6.0, )", - "Light.PortableResults.Validation": "[0.6.0, )" + "Light.PortableResults.AspNetCore.OpenApi": "[0.7.1, )", + "Light.PortableResults.Validation": "[0.7.1, )" } }, "light.portableresults.validation.openapi.sourcegeneration": { diff --git a/tests/Light.PortableResults.Validation.OpenApi.Tests/packages.lock.json b/tests/Light.PortableResults.Validation.OpenApi.Tests/packages.lock.json index d59c18e1..b2ea0bc3 100644 --- a/tests/Light.PortableResults.Validation.OpenApi.Tests/packages.lock.json +++ b/tests/Light.PortableResults.Validation.OpenApi.Tests/packages.lock.json @@ -243,33 +243,33 @@ "light.portableresults.aspnetcore.minimalapis": { "type": "Project", "dependencies": { - "Light.PortableResults.AspNetCore.Shared": "[0.6.0, )" + "Light.PortableResults.AspNetCore.Shared": "[0.7.1, )" } }, "light.portableresults.aspnetcore.openapi": { "type": "Project", "dependencies": { - "Light.PortableResults.AspNetCore.Shared": "[0.6.0, )", + "Light.PortableResults.AspNetCore.Shared": "[0.7.1, )", "Microsoft.AspNetCore.OpenApi": "[10.0.10, )" } }, "light.portableresults.aspnetcore.shared": { "type": "Project", "dependencies": { - "Light.PortableResults": "[0.6.0, )" + "Light.PortableResults": "[0.7.1, )" } }, "light.portableresults.validation": { "type": "Project", "dependencies": { - "Light.PortableResults": "[0.6.0, )" + "Light.PortableResults": "[0.7.1, )" } }, "light.portableresults.validation.openapi": { "type": "Project", "dependencies": { - "Light.PortableResults.AspNetCore.OpenApi": "[0.6.0, )", - "Light.PortableResults.Validation": "[0.6.0, )" + "Light.PortableResults.AspNetCore.OpenApi": "[0.7.1, )", + "Light.PortableResults.Validation": "[0.7.1, )" } }, "Microsoft.AspNetCore.OpenApi": { diff --git a/tests/Light.PortableResults.Validation.Tests/packages.lock.json b/tests/Light.PortableResults.Validation.Tests/packages.lock.json index 7be8cf0c..7b12af6a 100644 --- a/tests/Light.PortableResults.Validation.Tests/packages.lock.json +++ b/tests/Light.PortableResults.Validation.Tests/packages.lock.json @@ -244,7 +244,7 @@ "light.portableresults.validation": { "type": "Project", "dependencies": { - "Light.PortableResults": "[0.6.0, )" + "Light.PortableResults": "[0.7.1, )" } }, "Microsoft.Bcl.AsyncInterfaces": {