From d70e6c3bc4f48e668462000fecfc64c309a03987 Mon Sep 17 00:00:00 2001 From: Niko Nousiainen Date: Fri, 3 Apr 2026 01:01:21 +0300 Subject: [PATCH] removed unnecceru feature record for initial release of v3 --- .agents/skills/backend-guidance/SKILL.md | 62 +++ .agents/skills/coder/SKILL.md | 67 --- .agents/skills/coding-guidance-cpp/SKILL.md | 102 ++++ .../skills/development-contract-core/SKILL.md | 49 ++ .../references/policy-reference.md | 34 ++ .../run-release-checklist.example.sh | 100 ++++ .../frame-development-contract/SKILL.md | 64 +++ .agents/skills/project-core-dev/SKILL.md | 38 +- .../skills/project-platform-diagnose/SKILL.md | 19 +- .../project-release-maintainer/SKILL.md | 19 +- .../skills/project-vendor-boundary/SKILL.md | 20 +- .agents/skills/recursive-thinking/SKILL.md | 100 ++++ .agents/skills/thinking/SKILL.md | 113 +++- .agents/skills/ui-guidance/SKILL.md | 63 +++ .github/workflows/ci.yml | 22 + .github/workflows/release-checks.yml | 21 + AGENTS.md | 41 +- README.md | 64 ++- RELEASE_CHECKLIST.md | 9 + config/change-contract-policy.sh | 109 ++++ feature_records/.gitignore | 12 + feature_records/README.md | 27 + feature_records/TEMPLATE.md | 82 +++ .../2026-04-01-example-active-feature.md | 79 +++ .../done/2026-03-20-example-done-feature.md | 79 +++ ...development-contract-system-unification.md | 87 +++ .../2026-04-01-example-planned-feature.md | 82 +++ .../2026-03-10-example-superseded-feature.md | 79 +++ scripts/check-change-contracts.sh | 503 ++++++++++++++++++ scripts/check-release-hygiene.sh | 3 + scripts/init-project.sh | 6 + scripts/run-release-checklist.sh | 1 + scripts/set-feature-record-lifecycle.sh | 180 +++++++ scripts/test-change-contracts.sh | 107 ++++ scripts/test-feature-record-lifecycle.sh | 199 +++++++ tests/CMakeLists.txt | 8 + ..._risk_self_validation_requires_approver.md | 73 +++ .../impact_requires_evidence.md | 73 +++ .../change_contracts/invalid_uncertainty.md | 73 +++ .../mismatched_directory_state.md | 73 +++ .../missing_contract_section.md | 67 +++ .../missing_evidence_state.md | 73 +++ .../missing_plan_for_substantive_change.md | 73 +++ .../missing_verification_commands.md | 72 +++ .../change_contracts/missing_verifier.md | 73 +++ .../self_validation_with_waiver.md | 73 +++ .../self_validation_without_waiver.md | 73 +++ .../superseded_requires_pointer.md | 73 +++ .../change_contracts/valid_contract.md | 75 +++ .../waived_without_rationale.md | 73 +++ upcoming_features/.gitignore | 3 - upcoming_features/TEMPLATE.md | 23 - 52 files changed, 3550 insertions(+), 143 deletions(-) create mode 100644 .agents/skills/backend-guidance/SKILL.md delete mode 100644 .agents/skills/coder/SKILL.md create mode 100644 .agents/skills/coding-guidance-cpp/SKILL.md create mode 100644 .agents/skills/development-contract-core/SKILL.md create mode 100644 .agents/skills/development-contract-core/references/policy-reference.md create mode 100644 .agents/skills/development-contract-core/references/run-release-checklist.example.sh create mode 100644 .agents/skills/frame-development-contract/SKILL.md create mode 100644 .agents/skills/recursive-thinking/SKILL.md create mode 100644 .agents/skills/ui-guidance/SKILL.md create mode 100644 config/change-contract-policy.sh create mode 100644 feature_records/.gitignore create mode 100644 feature_records/README.md create mode 100644 feature_records/TEMPLATE.md create mode 100644 feature_records/active/2026-04-01-example-active-feature.md create mode 100644 feature_records/done/2026-03-20-example-done-feature.md create mode 100644 feature_records/done/2026-04-01-development-contract-system-unification.md create mode 100644 feature_records/planned/2026-04-01-example-planned-feature.md create mode 100644 feature_records/superseded/2026-03-10-example-superseded-feature.md create mode 100644 scripts/check-change-contracts.sh create mode 100644 scripts/set-feature-record-lifecycle.sh create mode 100644 scripts/test-change-contracts.sh create mode 100644 scripts/test-feature-record-lifecycle.sh create mode 100644 tests/fixtures/change_contracts/high_risk_self_validation_requires_approver.md create mode 100644 tests/fixtures/change_contracts/impact_requires_evidence.md create mode 100644 tests/fixtures/change_contracts/invalid_uncertainty.md create mode 100644 tests/fixtures/change_contracts/mismatched_directory_state.md create mode 100644 tests/fixtures/change_contracts/missing_contract_section.md create mode 100644 tests/fixtures/change_contracts/missing_evidence_state.md create mode 100644 tests/fixtures/change_contracts/missing_plan_for_substantive_change.md create mode 100644 tests/fixtures/change_contracts/missing_verification_commands.md create mode 100644 tests/fixtures/change_contracts/missing_verifier.md create mode 100644 tests/fixtures/change_contracts/self_validation_with_waiver.md create mode 100644 tests/fixtures/change_contracts/self_validation_without_waiver.md create mode 100644 tests/fixtures/change_contracts/superseded_requires_pointer.md create mode 100644 tests/fixtures/change_contracts/valid_contract.md create mode 100644 tests/fixtures/change_contracts/waived_without_rationale.md delete mode 100644 upcoming_features/.gitignore delete mode 100644 upcoming_features/TEMPLATE.md diff --git a/.agents/skills/backend-guidance/SKILL.md b/.agents/skills/backend-guidance/SKILL.md new file mode 100644 index 0000000..bf05fb2 --- /dev/null +++ b/.agents/skills/backend-guidance/SKILL.md @@ -0,0 +1,62 @@ +--- +name: backend-guidance +description: Overlay for server-side networked code — HTTP handlers, gRPC services, message consumers. Use alongside the repo's implementation skill when implementing or reviewing backend logic. +--- + +# Backend Guidance + +Read `AGENTS.md` first. This is a composable overlay, not a standalone workflow. +Use alongside the repo's implementation skill (e.g. **coding-guidance-cpp**, **project-core-dev**) +when the change touches backend code. + +## When to use + +The repo has server-side networked code: HTTP route handlers, gRPC service +methods, message/event consumers, or similar request-processing pipelines. + +## Not for + +HTTP client code, CLI tools that make outbound requests, batch processors, or +offline data pipelines. These do not have the handler/service/boundary shape +this skill addresses. + +## Rules + +- Keep handlers thin in responsibility, not by literal line count — parse + input, call a service function, map transport concerns, serialize output. If + a handler starts owning business decisions, extract that logic into a service + or core module. +- Keep business logic testable without transport — no HTTP context, no gRPC + metadata leaking into domain functions. +- Isolate data access behind an interface when it simplifies testing. Do not + add an abstraction layer when the data access is trivial or test-only. +- Validate and sanitize external input at the boundary, before it reaches + business logic. Internal calls between trusted modules do not need redundant + validation. +- Keep boundary-only concerns at the edge: authentication, authorization, + request decoding, transport-specific error mapping, and idempotency checks + where applicable. +- Use dependency injection where it makes tests simpler — not as a default + architectural pattern. + +## Decision Heuristics + +- **Handler size:** if a handler is hard to read in one screen or mixes + transport concerns with business decisions, it is doing too much. Extract the + logic; keep the handler as glue. +- **Test smell:** if testing a function requires standing up a server or faking + a transport layer, the function has a boundary problem. Move the logic + inward. +- **Validation placement:** validate once, at the outer edge. If you find + validation scattered across layers, consolidate it at the boundary. + +## Validation + +A backend change is done when (in addition to the base implementation skill's +validation): + +- handlers delegate to testable service functions +- business logic tests run without transport dependencies +- external input is validated at the boundary +- transport-specific error handling stays at the boundary instead of leaking + into domain logic diff --git a/.agents/skills/coder/SKILL.md b/.agents/skills/coder/SKILL.md deleted file mode 100644 index 35e73d6..0000000 --- a/.agents/skills/coder/SKILL.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -name: coder -description: Implementation skill for C++-first repos. Merges C++ engineering, code review discipline, optional backend layering guidance, and optional UI/frontend quality guidance. ---- - -# Coder - -Read `AGENTS.md` first. This skill adds implementation guidance only. - -## Default Stance - -- C++/CMake/testing are the primary focus -- Preserve buildability, deterministic tests, and analyzer cleanliness -- Prefer small explicit abstractions, RAII, const-correctness, narrow types, - and clear ownership boundaries -- Run the smallest validation set that proves the change, then extend as needed - -## Core Workflow - -1. Inspect the touched code, build shape, and tests. -2. Keep interfaces simple and behavior-oriented. -3. Implement the narrowest change that solves the problem. -4. Add or update tests close to the changed behavior using `frame_test.h` - macros (`FRAME_TEST`, `FRAME_EXPECT_EQ`, `FRAME_EXPECT_TRUE`, etc.). -5. Run `format` before committing, then run relevant build/test/analyzer - targets. - -## C++ Rules - -- Prefer rule-of-zero types and RAII over manual cleanup -- Treat raw pointers and references as non-owning unless ownership transfer is - explicit -- Avoid `new`/`delete`, C-style casts, silent narrowing, and unchecked bounds -- Prefer standard library value types, `std::array`, and explicit helper - structs over ambiguous adjacent parameters -- Keep warnings at zero in repo-owned code - -## Review Mindset - -When reviewing, prioritize findings over praise: - -- bugs and regressions -- ownership/lifetime issues -- missing validation or error handling -- missing tests -- security or performance risks with real impact - -Use severity ordering: `Critical`, `Important`, `Suggestion`. - -## Optional Backend Guidance - -If the repo contains networked or layered backend code: - -- keep routing/transport thin -- keep business logic in services/core modules -- keep data access isolated -- validate external input early -- use dependency injection where it simplifies tests - -## Optional UI/Frontend Guidance - -If the repo includes UI work: - -- preserve the existing design language unless asked to redesign -- prefer intentional visual direction over generic defaults -- make accessibility, layout stability, and responsive behavior part of done -- do not let styling work bypass tests/build hygiene diff --git a/.agents/skills/coding-guidance-cpp/SKILL.md b/.agents/skills/coding-guidance-cpp/SKILL.md new file mode 100644 index 0000000..4079387 --- /dev/null +++ b/.agents/skills/coding-guidance-cpp/SKILL.md @@ -0,0 +1,102 @@ +--- +name: coding-guidance-cpp +description: C++ implementation and review skill. Use when writing, modifying, or reviewing C++ code — feature work, bug fixes, refactors, or code review. Portable across C++ repos and build systems. +--- + +# C++ Coding Guidance + +Read `AGENTS.md` first. This skill adds C++ implementation and review guidance. + +## Adjacent Skills + +This skill provides portable C++ engineering principles. Compose with: + +- **Workflow:** **thinking** (planning), **recursive-thinking** (stress-testing), + **security** (threat modeling) +- **Domain overlays:** **backend-guidance** (server-side code), + **ui-guidance** (graphical UI/web frontend), + **project-core-dev** (repo-specific build/test commands) + +## Implementation Workflow + +1. Read the touched code, build shape, and existing tests. +2. Identify the narrowest change that solves the problem. +3. Implement with simple, behavior-oriented interfaces. +4. Add or update tests using the repo's established test framework, close to the + changed behavior. +5. Run the repo's formatter, then run relevant build, test, and analyzer targets. + +## Review Workflow + +When reviewing (not implementing), skip the implementation workflow and use this +instead: + +1. Read the change in full before commenting. +2. Identify findings, ordered by severity: `Critical` > `Important` > `Suggestion`. +3. Prioritize: bugs and regressions, ownership/lifetime issues, missing + validation or error handling, missing tests, security or performance risks + with real impact. +4. Report findings first. Skip praise unless the change is notably well-done. + +## C++ Rules + +### First tier — causes bugs + +- Treat raw pointers and references as non-owning; make ownership transfer + explicit in the type or documented in a comment +- Avoid `new`/`delete` — use RAII wrappers and smart pointers +- Prefer `std::unique_ptr` by default when single ownership is intended; use + `std::shared_ptr` only when shared lifetime is a real requirement +- Avoid unchecked bounds access — prefer `.at()`, range-for, or + iterator patterns over raw indexing when bounds are uncertain +- Avoid silent narrowing conversions — use explicit casts or `narrow_cast` + +### Second tier — prevents mistakes + +- Avoid C-style casts — use `static_cast`, `const_cast`, `reinterpret_cast` + to make intent visible +- Prefer rule-of-zero types; if you write a destructor, justify it +- Prefer `std::array` over C arrays, `std::string_view` and, where available, + `std::span` over raw pointer-plus-length pairs when lifetime rules are clear +- Prefer explicit helper structs over ambiguous adjacent parameters of the + same type +- Use `[[nodiscard]]` on functions where ignoring the return value is a bug +- Avoid unnecessary copies in range-for — use `const auto&` by default, but + prefer value iteration for cheap copy types or when ownership transfer is the + point +- Keep warnings at zero in repo-owned code + +## Decision Heuristics + +Use these when the right choice is not obvious: + +- **Scope check:** if a change touches more than 3 public interfaces, stop and + plan before continuing — the change is bigger than it looks. +- **Ownership clarity:** if resource ownership is not obvious from the type + signature alone, add a one-line comment documenting the contract. +- **Repo conventions:** if the repo has established rules for exceptions, + ownership types, containers, or error handling, follow them unless they cause + a correctness or safety problem. +- **Test setup size:** if test setup exceeds ~20 lines, extract a fixture — but + only if the setup is reused by at least 2 tests. +- **Narrowness vs. quality:** implement the narrowest change that solves the + problem. When narrowness conflicts with correctness or safety, prefer + correctness. When it conflicts with style, prefer narrowness unless + explicitly time-boxed for cleanup. +- **Refactor boundary:** when modifying a file, fix at most one small adjacent + issue. Do not refactor unrelated code in the same change. +- **Abstraction threshold:** three similar code blocks is a pattern; two is + coincidence. Do not extract a helper until the third occurrence unless the + duplication crosses a module boundary. + +## Validation + +A change is done when: + +- the code compiles without new warnings, unless the repo explicitly treats a + known warning set as baseline debt outside the change +- existing tests pass +- new or changed behavior has test coverage +- the repo's formatter has been run +- static analyzers (if configured) report no new findings +- review findings at `Critical` and `Important` severity are addressed diff --git a/.agents/skills/development-contract-core/SKILL.md b/.agents/skills/development-contract-core/SKILL.md new file mode 100644 index 0000000..f17963c --- /dev/null +++ b/.agents/skills/development-contract-core/SKILL.md @@ -0,0 +1,49 @@ +--- +name: development-contract-core +description: Portable workflow for repos that require tracked change contracts, verifier evidence, and smallest-proof validation. Use when a repo has a contract policy file or enforced feature-plan checker. +--- + +# Development Contract Core + +Read `AGENTS.md` first. Then find the repo's contract policy file and use it as the source of truth for repo-specific paths, plan locations, lanes, and validation profiles. + +## Use this skill when + +- work may trigger a tracked change contract +- a repo requires a plan update for substantive changes +- a checker validates plan structure, ownership, or evidence +- you need to choose the smallest proving validation set for a substantive change + +## Core workflow + +1. Read `AGENTS.md`, the touched files, and the repo contract policy file before editing. +2. Decide whether the change is substantive using policy data rather than guesswork. +3. If the change is substantive, create or update a non-template plan in the policy-defined plan directory. +4. Keep the plan aligned with the repo's enforced template, required evidence lanes, and lifecycle-directory rules. +5. Keep implementation ownership and verification ownership explicit. +6. Record verifier commands, observed results, and contract mismatches concretely. +7. Run the smallest validation profile that proves the change, then extend only when the surface justifies it. +8. Before closing work, run the repo's checker command and any additional checks implied by the chosen validation profile. + +## Decision rules + +- Treat the policy file as the single source of truth for substantive-path detection, plan location, section requirements, lane names, and default validation commands. +- If the repo ships a lifecycle transition helper, prefer it over manual file moves so path and lifecycle state stay synchronized. +- Do not duplicate repo literals across the skill, docs, checker, and template when policy can express them once. +- Keep the schema stable by default; use policy for repo-level variation and only change the schema when portability or correctness requires it. +- Prefer small repo overlays over forking the core skill. +- If a repo needs extra instructions that policy cannot express, add them in a thin overlay skill rather than bloating the core. + +## Output expectations + +When this skill applies, the final work should leave behind: + +- code or docs aligned with `AGENTS.md` +- a plan update when the change is substantive +- explicit verifier evidence +- a concise report of what was validated and what could not be validated + +## References + +- `references/policy-reference.md` for the portable policy surface and adoption rules +- `references/run-release-checklist.example.sh` for a portable pre-release runner scaffold diff --git a/.agents/skills/development-contract-core/references/policy-reference.md b/.agents/skills/development-contract-core/references/policy-reference.md new file mode 100644 index 0000000..b13a559 --- /dev/null +++ b/.agents/skills/development-contract-core/references/policy-reference.md @@ -0,0 +1,34 @@ +# Development Contract Policy Reference + +Use a small repo-local policy file as the source of truth for contract enforcement. + +## Minimum policy surface + +- plan directory +- template basename +- substantive path patterns +- substantive top-level files +- required section headings +- allowed lifecycle values +- allowed uncertainty/cost values +- evidence lane names +- checker command + +Prefer a repo-neutral path such as `config/change-contract-policy.sh`, when the repository itself, CI, or shell scripts depend on the policy. + +## Recommended additions + +- named validation profiles such as `docs`, `code`, and `release` +- repo-specific examples for install or smoke-test commands +- comments that explain why a path or lane is part of the contract +- a release-runner example kept as a skill reference, while the runnable repo copy lives under `scripts/` +- a repo-owned lifecycle helper when records live in state-specific subdirectories + +## Portability rules + +- keep policy data repo-local and declarative +- keep the core skill generic +- keep the checker reading the same policy file that humans read +- keep template and docs aligned with policy names +- keep lifecycle folder rules and any transition helper aligned with the checker +- prefer extending policy over cloning the core skill diff --git a/.agents/skills/development-contract-core/references/run-release-checklist.example.sh b/.agents/skills/development-contract-core/references/run-release-checklist.example.sh new file mode 100644 index 0000000..6e109c1 --- /dev/null +++ b/.agents/skills/development-contract-core/references/run-release-checklist.example.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash + +set -euo pipefail + +usage() { + cat <<'EOF_USAGE' +Usage: bash scripts/run-release-checklist.sh [--build-dir DIR] [--help] [-- ] + +Runs the automated portion of RELEASE_CHECKLIST.md up to, but not including, +manual release review. +EOF_USAGE +} + +die() { + printf 'ERROR: %s\n' "$*" >&2 + exit 1 +} + +note() { + printf '==> %s\n' "$*" +} + +run_cmd() { + note "$*" + "$@" +} + +run_build_target() { + shift + "$@" +} + +build_dir="" +declare -a extra_cmake_args=() + +while [[ $# -gt 0 ]]; do + case "$1" in + --build-dir) + [[ $# -ge 2 ]] || die "--build-dir requires a value" + build_dir="$2" + shift 2 + ;; + --help) + usage + exit 0 + ;; + --) + shift + extra_cmake_args=("$@") + break + ;; + *) + die "Unknown argument: $1" + ;; + esac +done + +if [[ -z "$build_dir" ]]; then + build_dir="$(mktemp -d /tmp/project-release-build-XXXXXX)" +fi + +declare -a generator_args=() +if command -v ninja >/dev/null 2>&1 || command -v ninja-build >/dev/null 2>&1; then + generator_args=(-G Ninja) +fi + +note "Release checklist build directory: $build_dir" +run_cmd bash scripts/check-release-hygiene.sh +run_cmd bash scripts/check-change-contracts.sh +run_cmd cmake -S . -B "$build_dir" "${generator_args[@]}" -DCMAKE_BUILD_TYPE=Debug "${extra_cmake_args[@]}" +run_build_target "$build_dir" cmake --build "$build_dir" -j"$(nproc)" +run_cmd ctest --test-dir "$build_dir" --output-on-failure +if command -v valgrind >/dev/null 2>&1; then + run_cmd bash scripts/run-valgrind.sh "$build_dir" +else + note "Skipping Valgrind because valgrind is not installed" +fi +if command -v clang-tidy >/dev/null 2>&1; then + run_build_target "$build_dir" cmake --build "$build_dir" --target clang-tidy +else + note "Skipping clang-tidy because clang-tidy is not installed" +fi +if command -v doxygen >/dev/null 2>&1; then + run_build_target "$build_dir" cmake --build "$build_dir" --target docs +else + note "Skipping docs because doxygen is not installed" +fi + +cat </...` plan when the change is substantive +- explicit verifier evidence in the plan +- repo policy, docs, and checker behavior that still agree +- a concise report of what was validated and what could not be validated + +## Examples + +- `Implement this feature in src/ and wire the tests`: + read `AGENTS.md`, read `config/change-contract-policy.sh`, update the relevant `feature_records/active/*.md` plan, implement narrowly, run the code profile, and record verifier commands/results. +- `Update the CI workflow to enforce a new repo rule`: + treat it as substantive workflow work under policy, update the plan, run the checker, and verify workflow/docs alignment. +- `Finish a feature and accept it`: + update verification evidence, then run `bash scripts/set-feature-record-lifecycle.sh feature_records/active/.md done`. +- `Port this contract system into another repo`: + copy the core skill, checker, template, and policy file, then edit the policy file before changing any repo-specific prose. diff --git a/.agents/skills/project-core-dev/SKILL.md b/.agents/skills/project-core-dev/SKILL.md index b8ac79e..52a81ed 100644 --- a/.agents/skills/project-core-dev/SKILL.md +++ b/.agents/skills/project-core-dev/SKILL.md @@ -1,24 +1,34 @@ --- name: project-core-dev -description: Generic core-dev overlay for repo-owned C++ code, tests, and top-level CMake changes. +description: Overlay for day-to-day feature work and bug fixes in repo-owned code. Provides a validation checklist for build, test, format, and analysis. Use alongside the repo's principle skill. --- # Project Core Dev -Read `AGENTS.md` first. Use this skill for normal feature work and bug fixes in -repo-owned C++ code. +Read `AGENTS.md` first. This is a composable overlay, not a standalone workflow. +Use alongside the repo's principle skill (e.g. **coding-guidance-cpp**) for +normal feature work and bug fixes in repo-owned code. -## Focus +## When to use -- preserve buildability, tests, docs, and analyzer cleanliness -- keep changes narrow and behavior-oriented -- prefer app-side fixes before widening platform or vendor boundaries +The task is a feature, bug fix, or refactor in repo-owned code that needs a +standard build-test-format-analyze validation pass. -## Validation +## Not for -- prefer `cmake --preset dev` for development builds -- run `ctest --output-on-failure` for covered changes -- run `cmake --build "$BUILD_DIR" --target format-check` before committing -- run `frame_cli --help` as a lightweight smoke test -- add docs/analyzer/Valgrind validation when the change surface justifies it -- run `cppcheck` target when available for additional static analysis +Vendored dependency changes (use **project-vendor-boundary**), release/packaging +work (use **project-release-maintainer**), config/test-focused work (use +**project-config-and-tests**), or environment diagnosis (use +**project-platform-diagnose**). + +## Validation Checklist + +Run the repo's equivalents of these steps before committing: + +- build with the development preset or debug configuration +- run the test suite with output on failure for covered changes +- run the formatter or format-check target +- run a lightweight smoke test (e.g. `--help` or `--version` on the main binary) +- add analyzer, sanitizer, or memory-checking validation when the change surface + justifies it +- run static analysis targets when available diff --git a/.agents/skills/project-platform-diagnose/SKILL.md b/.agents/skills/project-platform-diagnose/SKILL.md index 6a6ff48..0de4356 100644 --- a/.agents/skills/project-platform-diagnose/SKILL.md +++ b/.agents/skills/project-platform-diagnose/SKILL.md @@ -1,13 +1,26 @@ --- name: project-platform-diagnose -description: Generic overlay for environment-sensitive diagnosis such as service startup, install issues, platform integration, and runtime smoke checks. +description: Overlay for environment-sensitive diagnosis — service startup, install issues, platform integration, and runtime smoke checks. Portable across C++ projects. --- # Project Platform Diagnose -Read `AGENTS.md` first. Use this skill for environment-sensitive debugging. +Read `AGENTS.md` first. This is a diagnostic overlay, not a standalone workflow. +Use alongside the repo's implementation skill when debugging environment-sensitive +issues. -## Focus +## When to use + +The problem involves build/install failures, runtime environment issues, platform +integration, or headless/container behavior. + +## Not for + +Logic bugs in repo-owned code (use the implementation skill directly), vendored +dependency issues (use **project-vendor-boundary**), or config behavior (use +**project-config-and-tests**). + +## Rules - separate build/install failures from runtime failures - separate environment limits from app regressions diff --git a/.agents/skills/project-release-maintainer/SKILL.md b/.agents/skills/project-release-maintainer/SKILL.md index cf983ff..3139337 100644 --- a/.agents/skills/project-release-maintainer/SKILL.md +++ b/.agents/skills/project-release-maintainer/SKILL.md @@ -1,14 +1,27 @@ --- name: project-release-maintainer -description: Generic overlay for release-facing docs, install layout, workflows, licenses, and hygiene scripts. +description: Overlay for release-facing docs, install layout, workflows, licenses, and hygiene scripts. Portable across repos with a release/packaging pipeline. Use for publication-facing changes. --- # Project Release Maintainer -Read `AGENTS.md` first. Use this skill for publication-facing and +Read `AGENTS.md` first. This is a composable overlay, not a standalone workflow. +Use alongside the repo's implementation skill for publication-facing and packaging-sensitive changes. -## Focus +## When to use + +The change affects README, release checklists, install rules, shipped assets, +workflows, licenses, or other publication-facing artifacts. + +## Not for + +Internal code changes that don't affect the shipped surface (use the +implementation skill directly), vendored dependency work (use +**project-vendor-boundary**), or config/test work (use +**project-config-and-tests**). + +## Rules - keep README, release checklist, workflows, install rules, and shipped assets aligned - validate temporary install trees when install behavior changes diff --git a/.agents/skills/project-vendor-boundary/SKILL.md b/.agents/skills/project-vendor-boundary/SKILL.md index 88df79b..e269300 100644 --- a/.agents/skills/project-vendor-boundary/SKILL.md +++ b/.agents/skills/project-vendor-boundary/SKILL.md @@ -1,14 +1,26 @@ --- name: project-vendor-boundary -description: Generic overlay for app-owned versus vendored dependency boundaries. +description: Overlay for app-owned versus vendored dependency boundaries. Portable across repos that vendor third-party code. Use when work touches vendored dependencies or their integration seam. --- # Project Vendor Boundary -Read `AGENTS.md` first. Use this skill when work touches vendored dependencies -or their integration boundary. +Read `AGENTS.md` first. This is a composable overlay, not a standalone workflow. +Use alongside the repo's implementation skill when work touches vendored +dependencies or their integration boundary. -## Focus +## When to use + +The change involves vendored third-party code, the boundary between app-owned +and vendored code, or dependency integration (subtrees, vendor directories, +copied sources). + +## Not for + +App-owned code that does not touch vendor boundaries (use the implementation +skill directly), or release/packaging concerns (use **project-release-maintainer**). + +## Rules - prefer app-side integration changes before editing vendored code - treat vendored code as subtree/vendor content, not normal project code diff --git a/.agents/skills/recursive-thinking/SKILL.md b/.agents/skills/recursive-thinking/SKILL.md new file mode 100644 index 0000000..334a664 --- /dev/null +++ b/.agents/skills/recursive-thinking/SKILL.md @@ -0,0 +1,100 @@ +--- +name: recursive-thinking +description: Recursive self-questioning heuristic for stress-testing a plan, diagnosis, design, review, or recommendation before acting. Use when the user asks to use recursive thinking, go deeper, interrogate assumptions, pressure-test an approach, ask why/how questions, or find what would change the conclusion. Avoid for simple factual lookups or low-ambiguity tasks. +--- + +# Recursive Thinking + +Deliberate self-questioning to stress-test a recommendation, plan, diagnosis, or critique before acting. Not for simple lookups or low-ambiguity tasks. + +Adjacent skills: **brainstorming** (divergent generation) → **thinking** (convergent planning) → **recursive-thinking** (adversarial stress-testing) → **dream-thinking** (retrospective learning). + +A good pass should: sharpen the problem framing, expose a hidden assumption, identify a material risk, improve the decision path, or change the recommendation. If the recursion is not changing the work, compress or stop. + +Use this after **thinking** when a candidate plan, diagnosis, or recommendation exists and confidence needs to be challenged. +If there is no candidate object to stress-test yet, use **thinking** first. + +Typical chaining: +- use **thinking** to converge on a plan +- use **recursive-thinking** to challenge that plan when risk or ambiguity is high +- use **dream-thinking** after execution, conflict, or reflection-worthy experience + +## Understanding n + +`n` controls **breadth** (number of top-level questions), not **depth** (levels of follow-up). Depth is governed by insight yield. + +Do not treat `n` as recursion depth or expand every branch to `n` levels. This is selective iterative deepening: generate breadth, then deepen only the highest-yield branches. +Default to `n=4` or `n=5` unless the user asks otherwise. Higher `n` should widen coverage, not justify exhaustive expansion. + +## Core heuristic + +- generate up to `n` first-order questions covering meaningfully different angles +- answer each directly, grounded in observed facts where possible +- expand only the highest-yield branches (usually 1-3 unless asked for more) +- each expanded branch needs at least 2 levels of follow-up probes +- synthesize what changed and what should happen next + +## Grounding rules + +Mark each important claim as `observed`, `inferred`, or `unknown`. + +**Light grounding** (default for design, strategy, writing): +- tag claims, name unstated assumptions, say what would verify or falsify + +**Heavy grounding** (for diagnosis and review): +- `observed` must cite a source: `observed (file: path:line)`, `observed (git: hash)`, `observed (test output: result)` +- `inferred` must note derivation: `inferred (from: observed X + assumption Y)` +- `unknown` must note what would resolve it + +## Workflow + +1. **Identify** the object of analysis and define the success target (diagnosis, design, review, strategy, red-team, learning, decision, writing). +2. **Determine n.** Use the user's value, or ask, or pick a small default. +3. **Generate up to n first-order questions.** At least 2 must be adversarial — arguing against the obvious answer. Cover different angles: objective, constraints, evidence, alternatives, failure modes, incentives, interfaces, reversibility, verification. +4. **Honesty check.** Which question am I most uncomfortable answering? If it is not on the list, add it and drop the weakest. +5. **Score and answer.** Prefer branches that expose hidden assumptions, reduce uncertainty, change the plan, or eliminate bad options. Answer concretely. +6. **Recurse selectively.** Deepen with `but why`, `but how`, `but what evidence`, `but what fails`, `but what trade-off`, or `but what would change my mind` — whichever probe is sharpest. Do not force follow-ups on exhausted branches. +7. **Apply stopping tests.** Stop a branch when **all three** are true: + - **Delta:** no longer changes the recommendation or next action + - **Novelty:** introduces no new constraint, risk, or option + - **Evidence:** does not move any claim between unknown → inferred → observed +8. **Synthesize.** For each expanded branch, state what changed. Then give a final synthesis: strongest insights, key unknowns, confidence level, contradictions (named, not smoothed over), recommended next action, and what not to do yet. + +## Question design + +- Questions must be materially different, not rephrasings +- Replace weak questions instead of preserving the count mechanically +- Good branches change framing, confidence, risk, or validation; weak branches repeat known points + +## Task-mode presets + +- `diagnosis`: behavior, symptoms, reproduction, causal chain, observability, rollback, edge cases. **Heavy grounding.** +- `design`: objective, constraints, interfaces, alternatives, failure modes, reversibility, verification +- `review`: correctness, regression risk, hidden assumptions, missing tests, maintainability, safety. **Heavy grounding.** +- `strategy`: incentives, trade-offs, evidence, alternatives, downstream effects, decision thresholds +- `writing`: audience, claim strength, structure, evidence, ambiguity, consequences, tone +- `red-team`: strongest counter-argument, adversarial exploitation, weakest link, worst case, what are we refusing to consider +- `learning`: what do I not understand, where does my mental model break, what would a worked example show +- `decision`: options with trade-offs, uncertainty, reversibility, regret minimization, decision thresholds + +## Output shape + +- **n ≤ 5:** Compact. Light grounding unless diagnosis/review. +- **n > 5:** Start with a **tl;dr** (3-5 lines). Expand only highest-yield branches; one-line answers for the rest. + +Structure: tl;dr → n/object/target → questions with short answers → expanded branches → final synthesis. + +Each expanded branch: question → short answer → deeper probe → deeper answer → (continue until stopping tests trigger) → branch synthesis. + +## Anti-patterns + +**Mechanical:** generating all n×n×n nodes; abstract philosophy disconnected from the task; repeating the same concern reworded; using recursion to sound deep instead of changing the work. + +**Epistemic:** **confirmation deepening** (probing to reinforce rather than challenge — guard with adversarial questions); **authority laundering** (marking inferences as observed — guard with heavy grounding); **question gerrymandering** (avoiding questions that expose uncertainty — guard with honesty check); **synthesis whitewashing** (smoothing over contradictions — guard by naming them explicitly). + +## Examples + +- `Use recursive thinking with n=5 on whether this refactor should happen before or after the API change.` +- `Red-team this deployment plan with n=5 — what would go wrong?` +- `Do recursive why/how thinking on this bug report, focus on diagnosis.` +- Poor fit: `Use recursive thinking with n=10 to answer a simple factual question.` diff --git a/.agents/skills/thinking/SKILL.md b/.agents/skills/thinking/SKILL.md index b8e5ade..0484ac5 100644 --- a/.agents/skills/thinking/SKILL.md +++ b/.agents/skills/thinking/SKILL.md @@ -1,45 +1,122 @@ --- name: thinking -description: Planning and design skill. Merges brainstorming, concise planning, and kaizen-style incremental improvement. +description: Planning and design skill. Merges convergent planning, focused inquiry, and kaizen-style incremental improvement. Use for quick-to-medium planning tasks where structured thinking improves the outcome. Lighter than a full plan document — heavier than just asking the LLM to think. --- # Thinking -Read `AGENTS.md` first. This skill adds planning/design guidance. +If the repo has an `AGENTS.md`, read it first for repo conventions. + +Adjacent skills: **brainstorming** (divergent generation) → **thinking** (convergent planning) → **recursive-thinking** (adversarial stress-testing) → **dream-thinking** (retrospective learning). + +Use **thinking** when the main need is to converge on a practical plan. +Do not escalate to **recursive-thinking** unless the decision is high-risk, ambiguous, contentious, or likely to hide important assumptions. + +Typical chaining: +- use **thinking** to converge on a plan +- use **recursive-thinking** to challenge that plan when risk or ambiguity is high +- use **dream-thinking** after execution, conflict, or reflection-worthy experience ## Default Stance - understand the current repo shape before proposing changes -- prefer incremental, testable improvements over rewrites +- prefer incremental, testable improvements over rewrites — unless the complexity is structural (>3 files or ongoing maintenance burden), in which case compare rewrite cost against compounding workaround cost - keep plans implementation-ready and decision-complete - remove unnecessary complexity ruthlessly ## Workflow -1. Inspect the current code, docs, and constraints. -2. Clarify goal, success criteria, and out-of-scope items. -3. Compare 2-3 viable approaches when trade-offs matter. -4. Choose the narrowest approach that preserves quality. -5. Produce a concrete plan with validation and risk handling. +1. **Inspect** the current code, docs, and constraints. +2. **Clarify** goal, success criteria, and out-of-scope items. If ambiguous, ask one focused question. +3. **Compare** 2-3 approaches when the choice materially affects the outcome. + - Compare on: complexity, risk, reversibility, time-to-implement, dependency count. + - For each rejected approach, state why it lost in one line. + - If only one viable approach exists, say so and skip comparison. +4. **Choose** the approach with the least unnecessary complexity. + - "Narrowest" = fewest behavioral changes, smallest API surface, least cross-cutting risk. + - When narrowest conflicts with quality, prefer quality unless explicitly time-boxed. +5. **Produce** a concrete plan using the output structure below. + +## Plan Revision + +Watch for: blocked on something unanticipated, new constraint discovered during implementation, execution diverging from assumptions. When any fires: update the plan before continuing. + +## Planning Stop Test -## Brainstorming Rules +Planning is sufficient when all three are true: +- the next implementation step is concrete +- the main trade-off has been decided or explicitly deferred +- validation is clear enough to detect failure -- ask only the questions that materially change the design +## Focused Inquiry Rules + +- ask only questions that materially change the design - prefer one issue at a time over broad unfocused discovery - validate assumptions early when they affect architecture or rollout ## Kaizen Rules -- prefer small improvements that compound - make invalid states harder to express - use the type system, validation, and clear contracts to prevent mistakes -- leave touched code better than you found it, but avoid out-of-scope churn +- when modifying a file, fix at most one small adjacent issue — do not refactor unrelated code + +## Anti-patterns + +- inventing weak alternative approaches just to satisfy the comparison step +- asking broad discovery questions that do not change the plan +- producing a plan that sounds organized but leaves the next action ambiguous +- using "narrowest" to justify avoiding necessary quality improvements + +## Output Structure + +Scale to task size. + +**Small tasks** (single file, clear approach): + +``` +## Goal +One sentence. + +## Approach +What to change and why. + +## Validation +How to verify it works. +``` + +**Medium tasks** (multiple files, trade-offs): + +``` +## Goal +What we're trying to achieve and what success looks like. + +## Constraints +What's out of scope, what must not break. + +## Approach Comparison +| Approach | Pros | Cons | Risk | +|----------|------|------|------| +| A | ... | ... | ... | +| B | ... | ... | ... | + +Chosen: A, because [reason]. Not B, because [reason]. + +## Key Changes +By subsystem or file group. + +## Validation +Tests, manual checks, or acceptance criteria. + +## Assumptions +What we're taking as given. What would invalidate the plan. +``` -## Plan Rules +**Large tasks** (cross-cutting, architectural): Medium template plus: -A good plan should cover: +``` +## Phases +Ordered steps with dependencies noted. -- summary and intended outcome -- key changes by subsystem/behavior -- tests and validation -- explicit assumptions and defaults +## Rollback +What to do if a phase fails. +``` diff --git a/.agents/skills/ui-guidance/SKILL.md b/.agents/skills/ui-guidance/SKILL.md new file mode 100644 index 0000000..e2ccfe2 --- /dev/null +++ b/.agents/skills/ui-guidance/SKILL.md @@ -0,0 +1,63 @@ +--- +name: ui-guidance +description: Overlay for graphical UI and web frontend code. Use alongside the repo's implementation skill when implementing or reviewing UI changes. +--- + +# UI/Frontend Guidance + +Read `AGENTS.md` first. This is a composable overlay, not a standalone workflow. +Use alongside the repo's implementation skill (e.g. **coding-guidance-cpp**, **project-core-dev**) +when the change touches UI or frontend code. + +## When to use + +The repo includes graphical UI or web frontend code — web views, desktop UI, +embedded panels, or rendering layers. + +## Not for + +Terminal UIs (ncurses, TUI frameworks) unless the repo explicitly treats them as +UI with design standards. Pure data visualization or plotting libraries are also +out of scope. + +## Rules + +- Preserve the existing design language unless the task explicitly calls for + redesign. Do not introduce new color palettes, spacing systems, or component + patterns without justification. +- Accessibility, layout stability, and responsive behavior are part of done — + not follow-up work. +- UI changes must not bypass the repo's test and build hygiene. +- Verify visual output manually or with snapshot tests if the repo supports + them. +- If the repo does not document breakpoints, design tokens, or visual test + tooling, derive them from nearby UI code and record the fallback evidence in + any implementation or review note you produce. + +## Decision Heuristics + +- **Design language check:** before adding a new visual element, grep the + codebase for an existing element that serves a similar purpose. Match its + spacing, color tokens, and component structure unless the task requires + divergence. +- **Evidence fallback:** when the repo lacks UI docs, use the nearest existing + component or screen as the baseline and name the files inspected. +- **Accessibility bar:** if the change adds interactive elements, verify + keyboard navigation and screen reader labels. If the repo has no a11y + testing, add manual verification to the review checklist. +- **Layout stability:** if the change affects layout, test at the repo's + supported viewport sizes. If those are undocumented, test at the common + breakpoints already exercised by nearby code or styles. Flag layout shifts + that appear on resize or content change. + +## Validation + +A UI change is done when (in addition to the base implementation skill's +validation): + +- visual output matches the existing design language or the requested redesign +- interactive elements are keyboard-navigable +- layout is stable across supported viewports +- when automated UI verification is absent, any implementation or review note + you produce names the screenshots, snapshots, or manual checks used as + evidence diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 12ab2f0..ef0e96e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,10 +16,30 @@ jobs: steps: - name: Check out repository uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Resolve change contract range + id: contract_range + run: | + if [[ "${{ github.event_name }}" == "pull_request" ]]; then + echo "range=${{ github.event.pull_request.base.sha }}...${{ github.sha }}" >> "$GITHUB_OUTPUT" + elif [[ "${{ github.event.before }}" != "0000000000000000000000000000000000000000" ]]; then + echo "range=${{ github.event.before }}..${{ github.sha }}" >> "$GITHUB_OUTPUT" + else + echo "range=HEAD^..HEAD" >> "$GITHUB_OUTPUT" + fi - name: Run release hygiene checks + env: + FRAME_CHANGE_CONTRACT_RANGE: ${{ steps.contract_range.outputs.range }} run: bash scripts/check-release-hygiene.sh + - name: Run change contract checks + env: + FRAME_CHANGE_CONTRACT_RANGE: ${{ steps.contract_range.outputs.range }} + run: bash scripts/check-change-contracts.sh + build-test: runs-on: ubuntu-24.04 needs: hygiene @@ -27,6 +47,8 @@ jobs: steps: - name: Check out repository uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Install build dependencies run: | diff --git a/.github/workflows/release-checks.yml b/.github/workflows/release-checks.yml index 20fc195..84ee3ae 100644 --- a/.github/workflows/release-checks.yml +++ b/.github/workflows/release-checks.yml @@ -16,6 +16,17 @@ jobs: steps: - name: Check out repository uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Resolve change contract range + id: contract_range + run: | + if [[ "${{ github.event.before }}" != "0000000000000000000000000000000000000000" ]]; then + echo "range=${{ github.event.before }}..${{ github.sha }}" >> "$GITHUB_OUTPUT" + else + echo "range=HEAD^..HEAD" >> "$GITHUB_OUTPUT" + fi - name: Install build and diagnostics dependencies run: | @@ -28,6 +39,16 @@ jobs: pkg-config \ valgrind + - name: Run release hygiene checks + env: + FRAME_CHANGE_CONTRACT_RANGE: ${{ steps.contract_range.outputs.range }} + run: bash scripts/check-release-hygiene.sh + + - name: Run change contract checks + env: + FRAME_CHANGE_CONTRACT_RANGE: ${{ steps.contract_range.outputs.range }} + run: bash scripts/check-change-contracts.sh + - name: Configure debug build run: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_STANDARD=23 diff --git a/AGENTS.md b/AGENTS.md index 47d5b40..92b5bcb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,8 +17,16 @@ Baseline shape: contract - Qt/Clazy provide the example UI stack, not the baseline assumption - Public docs are generated from repo-owned headers and `docs/mainpage.md` -- Feature plans live under `upcoming_features/` as tracked Markdown files only - (see `upcoming_features/TEMPLATE.md` for the expected format) +- Feature plans live under `feature_records/` as tracked Markdown files + using lifecycle subdirectories such as `active/`, `planned/`, `done/`, and + `superseded/` (see `feature_records/TEMPLATE.md` for the expected format) +- `config/change-contract-policy.sh` is the repo-local source of truth for change- + contract paths, required sections, evidence lanes, and default validation + profiles +- Feature plans act as change contracts: they must declare maintained/new + contract points, lifecycle state, uncertainty/cost bands, implementer and + verifier ownership, an evidence matrix, implementation notes, verification + command/result notes, and any waivers explicitly - CMake presets provide named configurations for common workflows Repository principles: @@ -41,10 +49,12 @@ Repository principles: - `contrib/`: optional service/desktop integration examples - `cmake/`: reusable analyzer helper scripts - `.agents/skills/`: project-local agent overlays and merged skills +- `config/change-contract-policy.sh`: repo-local change-contract policy shared by + the checker and overlay skill - `.github/workflows/`: generic CI and release workflow templates - `benchmarks/`: optional chrono-based micro-benchmarks and `frame_bench.h` harness -- `upcoming_features/`: forward-looking implementation plans +- `feature_records/`: root template/README plus lifecycle-grouped feature records ## Build And Validation @@ -96,6 +106,7 @@ Use the smallest validation set that proves the change, then extend as needed: - `cmake --build "$BUILD_DIR" --target docs` - `bash scripts/run-valgrind.sh "$BUILD_DIR"` - `bash scripts/check-release-hygiene.sh` +- `bash scripts/check-change-contracts.sh` If install layout or shipped assets change, validate a temporary install prefix: @@ -205,11 +216,31 @@ This renames all placeholder targets, namespaces, prefixes, and filenames. ## Agent Workflow - Read `README.md` first, then the touched files before editing +- Read `config/change-contract-policy.sh` when the work may trigger a substantive + change contract - Prefer targeted changes over speculative cleanup - Keep `README.md`, `AGENTS.md`, `.agents/skills/`, scripts, and workflows aligned when the repo workflow changes -- Store forward-looking feature plans under `upcoming_features/` as tracked - Markdown files; update the existing plan rather than scattering notes +- Store tracked feature plans under `feature_records/` using the lifecycle + subdirectory that matches the plan's `State`; update the existing plan rather + than scattering notes +- Treat substantive repo-owned changes in `src/`, `tests/`, `scripts/`, `docs/`, + `.github/workflows/`, `cmake/`, `benchmarks/`, `contrib/`, and top-level + build/release docs as contract-bearing work that should update a non-template + feature plan unless `config/change-contract-policy.sh` narrows or extends that set +- Keep change-contract fields explicit: `missing` evidence states are not + acceptable, waived evidence requires rationale, and implementer/verifier + identity matches require a self-validation waiver +- Keep lifecycle state current so active, done, and superseded work can be + distinguished explicitly in `feature_records/` +- Prefer `bash scripts/set-feature-record-lifecycle.sh ` when + transitioning feature records between lifecycle folders +- When moving a record to `superseded`, provide a replacement path so + `Superseded by` stays explicit +- Keep implementation notes owned by the implementer and verification notes + owned by the verifier so the plan itself records the responsibility split +- Keep verifier notes concrete: record commands run, observed results, and any + contract mismatches rather than only a summary sentence - Do not leave generated artifacts in the repo tree - Do not assume the workspace is a valid Git repo; if Git commands fail, continue with file-based validation and note the limitation diff --git a/README.md b/README.md index 0c03881..2cd9484 100644 --- a/README.md +++ b/README.md @@ -56,8 +56,10 @@ Available presets: `dev` (debug + sanitizers), `release` (optimized + LTO), ## What This Frame Includes - `AGENTS.md`: canonical repo contract for build/test/tooling/release rules +- `config/change-contract-policy.sh`: repo-local change-contract policy shared by the checker and overlay skill - `.agents/skills/`: generic project-local skills for coding, documenting, - planning, security, release work, vendor-boundary work, and diagnostics + planning, security, release work, vendor-boundary work, diagnostics, a + portable development-contract core skill, and the repo-local overlay - `src/`: small example library + CLI - `tests/`: deterministic example tests, `CTest` registration, and `frame_test.h` micro-framework @@ -67,12 +69,52 @@ Available presets: `dev` (debug + sanitizers), `release` (optimized + LTO), - `contrib/`: optional system integration examples - `benchmarks/`: optional chrono-based micro-benchmark harness - `CMakePresets.json`: named build configurations for dev/release/ci/coverage -- `upcoming_features/`: tracked plan folder +- `feature_records/`: tracked feature-record folder with lifecycle subdirectories + with explicit lifecycle state, change contracts, uncertainty/cost bands, + responsibilities, and evidence matrices + +## Feature Record Lifecycle + +Human-readable state flow: + +```text + start work complete + verify + +-----------+ ----------> +----------+ -----------------> +--------+ + | planned/ | | active/ | | done/ | + +-----------+ +----------+ +--------+ + | + | replace with newer record + v + +---------------+ + | superseded/ | + +---------------+ + + rule: records in `superseded/` must set `Superseded by` to the replacement record +``` + +How to read it: + +- `planned/` means approved or drafted work that has not started. +- `active/` means work is currently in progress. +- `done/` means work completed and remains as project history. +- `superseded/` means the old record was replaced by a newer one. + +Use the helper to move records safely: + +```bash +bash scripts/set-feature-record-lifecycle.sh feature_records/planned/.md active +bash scripts/set-feature-record-lifecycle.sh feature_records/active/.md done +bash scripts/set-feature-record-lifecycle.sh \ + --superseded-by feature_records/done/.md \ + feature_records/active/.md superseded +``` ## Project Layout ```text .agents/skills/ Project-local agent skills +config/change-contract-policy.sh + Repo-local change-contract policy .github/workflows/ Generic CI and release workflow templates benchmarks/ Optional micro-benchmark harness cmake/ Analyzer helper scripts @@ -81,7 +123,7 @@ docs/ Doxygen config and API-focused docs scripts/ Hygiene, release, diagnostics, and init helpers src/ Example library and CLI tests/ Deterministic example tests and frame_test.h -upcoming_features/ Forward-looking implementation plans +feature_records/ Root template, README, and lifecycle subdirectories ``` ## Tooling @@ -133,11 +175,27 @@ Typical first steps: 4. Trim or extend `.agents/skills/` to match the real project. 5. Adapt workflows, scripts, and `contrib/` assets to the real runtime shape. 6. Add or replace the example tests with project-specific deterministic tests. +7. Treat `feature_records/` plans as tracked change contracts for substantive + repo-owned work. Changes to repo-owned code, tests, scripts, docs, workflows, + or top-level build/release files should update a non-template plan file. + Place each record under the lifecycle directory that matches its `State`. +8. Keep `config/change-contract-policy.sh` as the source of truth for substantive + path detection, required plan sections, evidence lanes, and default + validation profiles. +9. Keep verifier evidence concrete: plans should record verifier commands, + observed results, and any contract mismatches rather than only a prose note. +10. Use `bash scripts/set-feature-record-lifecycle.sh ` to + move records between lifecycle folders deterministically. ## Release And Docs - Use [RELEASE_CHECKLIST.md](RELEASE_CHECKLIST.md) before publishing. - The Doxygen main page lives in [docs/mainpage.md](docs/mainpage.md). +- Keep `feature_records/README.md`, `feature_records/TEMPLATE.md`, and lifecycle + records under `feature_records/*/*.md` aligned with the enforced change-contract + template and `config/change-contract-policy.sh` so lifecycle state, uncertainty, + cost, ownership, the evidence matrix, and verifier command/result notes + remain explicit. - Keep publication-facing files free of machine-specific paths, broken local links, and generated artifacts. diff --git a/RELEASE_CHECKLIST.md b/RELEASE_CHECKLIST.md index e96ad94..6fd1dfd 100644 --- a/RELEASE_CHECKLIST.md +++ b/RELEASE_CHECKLIST.md @@ -9,11 +9,20 @@ Use this checklist before publishing the repository or cutting a release. ```bash bash scripts/check-release-hygiene.sh +bash scripts/check-change-contracts.sh ``` - Verify there are no machine-specific home-directory paths or absolute local Markdown links in tracked files. - Confirm no generated binaries or large release payloads are tracked in Git. +- Confirm tracked feature plans keep explicit contract, uncertainty/cost, + lifecycle state, responsibilities, evidence matrices, implementation notes, + verification command/result notes, and waiver rationale. +- If any record changed lifecycle, prefer `bash scripts/set-feature-record-lifecycle.sh` + over manual moves so the state field and directory stay aligned. +- Confirm `config/change-contract-policy.sh`, `feature_records/README.md`, + `feature_records/TEMPLATE.md`, and + `scripts/check-change-contracts.sh` still describe the same contract. ## Licensing And Provenance diff --git a/config/change-contract-policy.sh b/config/change-contract-policy.sh new file mode 100644 index 0000000..ee45241 --- /dev/null +++ b/config/change-contract-policy.sh @@ -0,0 +1,109 @@ +#!/usr/bin/env bash + +# Repo-local change-contract policy consumed by the checker and overlay skill. + +FRAME_CONTRACT_PLAN_DIR="feature_records" +FRAME_CONTRACT_TEMPLATE_BASENAME="TEMPLATE.md" + +FRAME_CONTRACT_SUBSTANTIVE_PATH_PATTERNS=( + 'src/*' + 'tests/*' + 'scripts/*' + 'cmake/*' + 'docs/*' + 'benchmarks/*' + 'contrib/*' + '.github/workflows/*' +) + +FRAME_CONTRACT_SUBSTANTIVE_TOP_LEVEL_FILES=( + 'CMakeLists.txt' + 'CMakePresets.json' + 'README.md' + 'AGENTS.md' + 'RELEASE_CHECKLIST.md' +) + +FRAME_CONTRACT_REQUIRED_SECTIONS=( + '## Motivation' + '## Proposed Behavior' + '## Lifecycle' + '## Contract' + '## Uncertainty And Cost' + '## Responsibilities' + '## Evidence Matrix' + '## Implementation Notes' + '## Verification Notes' + '## Files to Add/Modify' + '## Testing Strategy' + '## Waivers' +) + +FRAME_CONTRACT_LIFECYCLE_VALUES=( + 'planned' + 'active' + 'superseded' + 'done' +) + +FRAME_CONTRACT_UNCERTAINTY_VALUES=( + 'low' + 'medium' + 'high' +) + +FRAME_CONTRACT_EVIDENCE_STATUS_VALUES=( + 'passed' + 'waived' + 'not_applicable' + 'missing' +) + +FRAME_CONTRACT_YES_NO_VALUES=( + 'yes' + 'no' +) + +FRAME_CONTRACT_IMPLEMENTATION_STATUS_VALUES=( + 'planned' + 'in_progress' + 'completed' +) + +FRAME_CONTRACT_VERIFICATION_STATUS_VALUES=( + 'pending' + 'in_progress' + 'completed' +) + +FRAME_CONTRACT_EVIDENCE_LANES=( + 'Tests' + 'Docs' + 'Analyzers' + 'Install validation' + 'Release hygiene' +) + +FRAME_CONTRACT_CHECKER_COMMAND='bash scripts/check-change-contracts.sh' + +FRAME_CONTRACT_VALIDATION_PROFILE_DOCS=( + 'bash scripts/check-change-contracts.sh' + 'bash scripts/check-release-hygiene.sh' +) + +FRAME_CONTRACT_VALIDATION_PROFILE_CODE=( + 'bash scripts/check-change-contracts.sh' + 'cmake --preset dev' + 'cmake --build --preset dev' + 'ctest --preset dev' + './build/dev/frame_cli --help' + 'cmake --build build/dev --target format-check' + 'cmake --build build/dev --target lint' +) + +FRAME_CONTRACT_VALIDATION_PROFILE_RELEASE=( + 'bash scripts/check-change-contracts.sh' + 'bash scripts/check-release-hygiene.sh' + 'cmake --build build/dev --target docs' + 'bash scripts/run-valgrind.sh build/dev' +) diff --git a/feature_records/.gitignore b/feature_records/.gitignore new file mode 100644 index 0000000..7c5f270 --- /dev/null +++ b/feature_records/.gitignore @@ -0,0 +1,12 @@ +* +!.gitignore +!README.md +!TEMPLATE.md +!active/ +!planned/ +!done/ +!superseded/ +!active/*.md +!planned/*.md +!done/*.md +!superseded/*.md diff --git a/feature_records/README.md b/feature_records/README.md new file mode 100644 index 0000000..2e3d906 --- /dev/null +++ b/feature_records/README.md @@ -0,0 +1,27 @@ +# Feature Records + +This directory stores tracked change contracts for substantive repo-owned work. + +Lifecycle folders: + +- `planned/` contains approved or drafted work that has not started yet. +- `active/` contains work currently in flight. +- `done/` contains completed records that remain part of the repo history. +- `superseded/` contains historical records replaced by a newer record. + +Rules: + +- A record's folder must match its `## Lifecycle` `State`. +- `TEMPLATE.md` stays at the root and is used for all new records. +- `superseded/` records must still point at their replacement via `Superseded by`. +- Substantive repo changes must update a non-template record under one of the lifecycle folders. +- Use `bash scripts/set-feature-record-lifecycle.sh ` to move a + record between lifecycle folders and update its `Lifecycle` state field. +- When moving to `superseded`, pass `--superseded-by feature_records//.md`. + +Examples included: + +- `planned/2026-04-01-example-planned-feature.md` +- `active/2026-04-01-example-active-feature.md` +- `done/2026-03-20-example-done-feature.md` +- `superseded/2026-03-10-example-superseded-feature.md` diff --git a/feature_records/TEMPLATE.md b/feature_records/TEMPLATE.md new file mode 100644 index 0000000..2748a6f --- /dev/null +++ b/feature_records/TEMPLATE.md @@ -0,0 +1,82 @@ +# Feature: + +This template is enforced by `config/change-contract-policy.sh` and `scripts/check-change-contracts.sh`. + +Store each completed record under the lifecycle directory that matches its +`## Lifecycle` `State`. +Prefer `bash scripts/set-feature-record-lifecycle.sh ` when +changing lifecycle folders after a record already exists. + +## Motivation + +Why this feature is needed and what problem it solves. + +## Proposed Behavior + +What the feature does from the user's perspective. Include examples if helpful. + +## Lifecycle + +- State: planned|active|superseded|done +- Supersedes: none| +- Superseded by: none| + +## Contract + +- Must remain true: +- Must become true: +- Success signals: + +## Uncertainty And Cost + +- Product uncertainty: low|medium|high +- Technical uncertainty: low|medium|high +- Implementation cost: low|medium|high +- Validation cost: low|medium|high +- Notes: + +## Responsibilities + +- Implementer: +- Verifier: +- Approver: + +## Evidence Matrix + +- Tests | impact=yes|no | status=passed|waived|not_applicable|missing | rationale= | verifier_note= +- Docs | impact=yes|no | status=passed|waived|not_applicable|missing | rationale= | verifier_note= +- Analyzers | impact=yes|no | status=passed|waived|not_applicable|missing | rationale= | verifier_note= +- Install validation | impact=yes|no | status=passed|waived|not_applicable|missing | rationale= | verifier_note= +- Release hygiene | impact=yes|no | status=passed|waived|not_applicable|missing | rationale= | verifier_note= + +## Implementation Notes + +- Owner: +- Status: planned|in_progress|completed +- Notes: + +## Verification Notes + +- Owner: +- Status: pending|in_progress|completed +- Commands: +- Observed result: +- Contract mismatches: none| + +## Waivers + +- Self-validation rationale: none + +## Files to Add/Modify + +- `src/...` — description of changes +- `tests/...` — test coverage plan +- `CMakeLists.txt` — build integration (if applicable) + +## Testing Strategy + +How to verify the feature works correctly. Prefer deterministic, headless tests. + +## Open Questions + +Unresolved decisions or trade-offs that need input before implementation. diff --git a/feature_records/active/2026-04-01-example-active-feature.md b/feature_records/active/2026-04-01-example-active-feature.md new file mode 100644 index 0000000..92719dd --- /dev/null +++ b/feature_records/active/2026-04-01-example-active-feature.md @@ -0,0 +1,79 @@ +# Feature: Example Active Feature + +This example record demonstrates how an in-flight item should look in +`feature_records/active/`. + +## Motivation + +Repo browsers should be able to identify active work immediately from the +directory tree and from a concrete example record. + +## Proposed Behavior + +Track an example feature that is currently in progress and uses the `active` +lifecycle state. + +## Lifecycle + +- State: active +- Supersedes: none +- Superseded by: none + +## Contract + +- Must remain true: Active records should communicate that work is underway and still needs final verification or completion. +- Must become true: The repository should include a valid `active` example for browsing and onboarding. +- Success signals: Browsers can open `feature_records/active/` and see a record that reads like in-flight work rather than a finished historical note. + +## Uncertainty And Cost + +- Product uncertainty: low +- Technical uncertainty: low +- Implementation cost: low +- Validation cost: low +- Notes: This example exists to show folder semantics rather than to describe shipped behavior. + +## Responsibilities + +- Implementer: example-author +- Verifier: example-reviewer +- Approver: none + +## Evidence Matrix + +- Tests | impact=no | status=not_applicable | rationale=The example does not represent a real executable change. | verifier_note=Example-only record. +- Docs | impact=yes | status=passed | rationale=The example is itself documentation for the active lifecycle shape. | verifier_note=Reviewed for consistency with the template and folder naming. +- Analyzers | impact=no | status=not_applicable | rationale=No analyzer-relevant source changes are represented by this example. | verifier_note=Example-only record. +- Install validation | impact=no | status=not_applicable | rationale=Install behavior is out of scope for this example. | verifier_note=Example-only record. +- Release hygiene | impact=no | status=not_applicable | rationale=The example does not change release automation behavior. | verifier_note=Example-only record. + +## Implementation Notes + +- Owner: example-author +- Status: in_progress +- Notes: This example intentionally reads like work that has started but is not yet complete. + +## Verification Notes + +- Owner: example-reviewer +- Status: in_progress +- Commands: checker review pending final completion +- Observed result: The example currently illustrates the in-flight shape of an active record. +- Contract mismatches: none + +## Waivers + +- Self-validation rationale: none + +## Files to Add/Modify + +- `feature_records/...` — example lifecycle documentation only + +## Testing Strategy + +Keep the record checker passing while the example remains in the `active` +folder. + +## Open Questions + +None. diff --git a/feature_records/done/2026-03-20-example-done-feature.md b/feature_records/done/2026-03-20-example-done-feature.md new file mode 100644 index 0000000..e109e08 --- /dev/null +++ b/feature_records/done/2026-03-20-example-done-feature.md @@ -0,0 +1,79 @@ +# Feature: Example Done Feature + +This example record demonstrates how a completed item should look in +`feature_records/done/`. + +## Motivation + +Repo browsers should be able to distinguish completed work from active work +without opening every record individually. + +## Proposed Behavior + +Track an example feature that has already completed and passed its expected +documentation-level checks. + +## Lifecycle + +- State: done +- Supersedes: none +- Superseded by: none + +## Contract + +- Must remain true: Done records should remain in the repository as historical evidence rather than being deleted. +- Must become true: The repository should include a valid `done` example for browsing and onboarding. +- Success signals: Browsers can open `feature_records/done/` and see a finished record with completed implementation and verification notes. + +## Uncertainty And Cost + +- Product uncertainty: low +- Technical uncertainty: low +- Implementation cost: low +- Validation cost: low +- Notes: This is a stable example that illustrates the completed lifecycle state. + +## Responsibilities + +- Implementer: example-author +- Verifier: example-reviewer +- Approver: none + +## Evidence Matrix + +- Tests | impact=no | status=not_applicable | rationale=The example does not represent a real executable feature. | verifier_note=Example-only record. +- Docs | impact=yes | status=passed | rationale=The purpose of the record is to document the `done` lifecycle shape. | verifier_note=Reviewed for template consistency and completed-state readability. +- Analyzers | impact=no | status=not_applicable | rationale=No analyzer-relevant source changes are represented by this example. | verifier_note=Example-only record. +- Install validation | impact=no | status=not_applicable | rationale=Install behavior is out of scope for this example. | verifier_note=Example-only record. +- Release hygiene | impact=no | status=not_applicable | rationale=The example does not change release behavior. | verifier_note=Example-only record. + +## Implementation Notes + +- Owner: example-author +- Status: completed +- Notes: This example is complete and intentionally remains in the repository as historical reference. + +## Verification Notes + +- Owner: example-reviewer +- Status: completed +- Commands: example record review +- Observed result: The record demonstrates a completed lifecycle state with finished notes and no pending work. +- Contract mismatches: none + +## Waivers + +- Self-validation rationale: none + +## Files to Add/Modify + +- `feature_records/...` — example lifecycle documentation only + +## Testing Strategy + +Keep the checker passing and leave the record in `done/` as a permanent +historical example. + +## Open Questions + +None. diff --git a/feature_records/done/2026-04-01-development-contract-system-unification.md b/feature_records/done/2026-04-01-development-contract-system-unification.md new file mode 100644 index 0000000..b8dd242 --- /dev/null +++ b/feature_records/done/2026-04-01-development-contract-system-unification.md @@ -0,0 +1,87 @@ +# Feature: Development Contract System Unification + +## Motivation + +The current worktree no longer represents a few isolated slices. It now adds a +complete repo-owned development contract system: policy, feature-record +lifecycle layout, checker, lifecycle helper, examples, docs, and a portable +combined skill. The contract record should match that reality instead of +pretending the changes are still separate active efforts. + +## Proposed Behavior + +Treat the current uncommitted repository changes as one active feature that +establishes the full development contract system. The system should provide a +repo-owned policy file, lifecycle-grouped `feature_records/`, a checker, a +lifecycle transition helper, seed examples, aligned docs, and a portable skill +that explains how to build the same system in another repository. + +## Lifecycle + +- State: done +- Supersedes: none +- Superseded by: none + +## Contract + +- Must remain true: The development contract system stays repo-owned, deterministic, Markdown-based, and enforceable through local scripts and CI. +- Must become true: The current worktree is represented by a single active record that covers policy, lifecycle layout, transition helper, docs, and portable skill guidance together. +- Success signals: One active record describes the in-flight development contract system, the example lifecycle records remain browseable, and the checker plus repo hygiene lanes still pass. + +## Uncertainty And Cost + +- Product uncertainty: low +- Technical uncertainty: medium +- Implementation cost: medium +- Validation cost: medium +- Notes: The system shape is now clear, but the repository still has one large uncommitted slice spanning scripts, docs, skills, and record layout. + +## Responsibilities + +- Implementer: codex +- Verifier: codex +- Approver: repo-maintainer + +## Evidence Matrix + +- Tests | impact=yes | status=passed | rationale=The checker and lifecycle helper are core workflow behavior and need direct shell coverage. | verifier_note=Validated with `bash scripts/test-feature-record-lifecycle.sh` and `bash scripts/test-change-contracts.sh`. +- Docs | impact=yes | status=passed | rationale=README, AGENTS, release checklist, feature-record docs, and skills all changed as part of this unified workflow slice. | verifier_note=Reviewed README, AGENTS, RELEASE_CHECKLIST, feature_records docs, and contract-related skills for alignment. +- Analyzers | impact=no | status=not_applicable | rationale=The current work is shell, Markdown, and repo workflow structure rather than C++ analyzer-sensitive source changes. | verifier_note=No analyzer lane was required for this slice. +- Install validation | impact=no | status=not_applicable | rationale=Install layout and shipped runtime assets are unchanged. | verifier_note=Install validation remained out of scope. +- Release hygiene | impact=yes | status=passed | rationale=This feature changes repo process and must remain aligned with hygiene enforcement. | verifier_note=Validated with `bash scripts/check-change-contracts.sh` and `bash scripts/check-release-hygiene.sh`. + +## Implementation Notes + +- Owner: codex +- Status: in_progress +- Notes: Consolidated the current worktree under one umbrella contract and removed intermediate superseded real records so the initial commit stays simpler. + +## Verification Notes + +- Owner: codex +- Status: completed +- Commands: `bash scripts/test-feature-record-lifecycle.sh`; `bash scripts/test-change-contracts.sh`; `bash scripts/check-change-contracts.sh`; `bash scripts/check-release-hygiene.sh` +- Observed result: The helper tests, checker fixtures, direct checker run, and release hygiene lane all pass with a single real active contract plus the example lifecycle records. +- Contract mismatches: none + +## Waivers + +- Self-validation rationale: This repo-owned workflow change is being implemented and verified in one session, with explicit verifier evidence recorded. + +## Files to Add/Modify + +- `config/...` — repo-owned contract policy +- `feature_records/...` — lifecycle tree, examples, and active contract records +- `scripts/...` — checker, lifecycle helper, release helpers, and shell tests +- `README.md`, `AGENTS.md`, `RELEASE_CHECKLIST.md` — maintainer-facing workflow guidance +- `.agents/skills/...` — repo-local overlays and the portable combined skill + +## Testing Strategy + +Keep the checker fixture suite, lifecycle-helper shell tests, direct checker +run, and release hygiene lane passing while the unified feature remains active. + +## Open Questions + +Whether the next slice should add a helper for creating new feature records from +the template, not just transitioning existing ones. diff --git a/feature_records/planned/2026-04-01-example-planned-feature.md b/feature_records/planned/2026-04-01-example-planned-feature.md new file mode 100644 index 0000000..7b8b967 --- /dev/null +++ b/feature_records/planned/2026-04-01-example-planned-feature.md @@ -0,0 +1,82 @@ +# Feature: Example Planned Feature + +This example record demonstrates how a not-yet-started item should look in +`feature_records/planned/`. + +## Motivation + +Repo browsers should be able to see what a planned record looks like without +having to infer it from the template alone. + +## Proposed Behavior + +Add a small example feature record that stays in the `planned/` folder and +shows a not-yet-started change contract. + +## Lifecycle + +- State: planned +- Supersedes: none +- Superseded by: none + +## Contract + +- Must remain true: Planned records should be explicit about intent, scope, and expected proof before work starts. +- Must become true: The repository should include an example record that demonstrates the `planned` lifecycle state. +- Success signals: Browsers can open `feature_records/planned/` and see a valid example without needing to interpret placeholders. + +## Uncertainty And Cost + +- Product uncertainty: low +- Technical uncertainty: low +- Implementation cost: low +- Validation cost: low +- Notes: This is documentation-by-example rather than a live implementation slice. + +## Responsibilities + +- Implementer: example-author +- Verifier: example-reviewer +- Approver: none + +## Evidence Matrix + +- Tests | impact=no | status=not_applicable | rationale=This example documents lifecycle shape rather than executable behavior. | verifier_note=Example-only record. +- Docs | impact=yes | status=passed | rationale=The purpose of the record is to document the planned lifecycle shape. | verifier_note=Reviewed for template and lifecycle consistency. +- Analyzers | impact=no | status=not_applicable | rationale=No analyzer-relevant source changes are represented by this example. | verifier_note=Example-only record. +- Install validation | impact=no | status=not_applicable | rationale=Install behavior is out of scope for this example. | verifier_note=Example-only record. +- Release hygiene | impact=no | status=not_applicable | rationale=The record is illustrative and does not change release behavior. | verifier_note=Example-only record. + +## Implementation Notes + +- Owner: example-author +- Status: planned +- Notes: Example placeholder for a feature that has not started implementation. + +## Verification Notes + +- Owner: example-reviewer +- Status: pending +- Commands: pending +- Observed result: Pending because the example represents a record before work begins. +- Contract mismatches: none + +## Waivers + +- Self-validation rationale: none + +## Files to Add/Modify + +- `src/...` — example future implementation surface +- `tests/...` — example future validation surface +- `README.md` — optional future documentation update + +## Testing Strategy + +When the feature starts, validate it with the smallest proving set and update +this record from `planned/` to the appropriate lifecycle folder. + +## Open Questions + +Whether the eventual implementation should remain docs-only or grow into a +real workflow helper. diff --git a/feature_records/superseded/2026-03-10-example-superseded-feature.md b/feature_records/superseded/2026-03-10-example-superseded-feature.md new file mode 100644 index 0000000..4707618 --- /dev/null +++ b/feature_records/superseded/2026-03-10-example-superseded-feature.md @@ -0,0 +1,79 @@ +# Feature: Example Superseded Feature + +This example record demonstrates how replaced work should look in +`feature_records/superseded/`. + +## Motivation + +Repo browsers should be able to see that some historical records were replaced +by newer ones, not merely completed. + +## Proposed Behavior + +Track an example record whose original plan was replaced by a newer record in +`feature_records/done/`. + +## Lifecycle + +- State: superseded +- Supersedes: none +- Superseded by: feature_records/done/2026-03-20-example-done-feature.md + +## Contract + +- Must remain true: Superseded records should remain readable as historical context and should clearly point at their replacement. +- Must become true: The repository should include a valid `superseded` example that demonstrates the replacement link. +- Success signals: Browsers can open `feature_records/superseded/` and immediately see which newer record replaced it. + +## Uncertainty And Cost + +- Product uncertainty: low +- Technical uncertainty: low +- Implementation cost: low +- Validation cost: low +- Notes: This is a documentation example showing the superseded lifecycle relationship. + +## Responsibilities + +- Implementer: example-author +- Verifier: example-reviewer +- Approver: none + +## Evidence Matrix + +- Tests | impact=no | status=not_applicable | rationale=The example does not represent executable behavior. | verifier_note=Example-only record. +- Docs | impact=yes | status=passed | rationale=The example exists to document how a superseded record should look. | verifier_note=Reviewed for template consistency and replacement-pointer clarity. +- Analyzers | impact=no | status=not_applicable | rationale=No analyzer-relevant source changes are represented by this example. | verifier_note=Example-only record. +- Install validation | impact=no | status=not_applicable | rationale=Install behavior is out of scope for this example. | verifier_note=Example-only record. +- Release hygiene | impact=no | status=not_applicable | rationale=The example does not change release behavior. | verifier_note=Example-only record. + +## Implementation Notes + +- Owner: example-author +- Status: completed +- Notes: This example remains in the repository only as historical context after being replaced. + +## Verification Notes + +- Owner: example-reviewer +- Status: completed +- Commands: example record review +- Observed result: The record clearly points to its replacement in `feature_records/done/2026-03-20-example-done-feature.md`. +- Contract mismatches: none + +## Waivers + +- Self-validation rationale: none + +## Files to Add/Modify + +- `feature_records/...` — example lifecycle documentation only + +## Testing Strategy + +Keep the checker passing and preserve the `Superseded by` pointer to the +replacement record. + +## Open Questions + +None. diff --git a/scripts/check-change-contracts.sh b/scripts/check-change-contracts.sh new file mode 100644 index 0000000..da48d74 --- /dev/null +++ b/scripts/check-change-contracts.sh @@ -0,0 +1,503 @@ +#!/usr/bin/env bash + +set -euo pipefail + +policy_path="${FRAME_CONTRACT_POLICY_FILE:-config/change-contract-policy.sh}" +repo_root="${1:-$(pwd)}" +policy_file="$repo_root/$policy_path" + +if [[ ! -f "$policy_file" ]]; then + printf 'ERROR: missing contract policy file: %s\n' "$policy_file" >&2 + exit 1 +fi + +source "$policy_file" + +plan_dir="${FRAME_CONTRACT_PLAN_DIR:-feature_records}" +template_basename="${FRAME_CONTRACT_TEMPLATE_BASENAME:-TEMPLATE.md}" +features_dir="$repo_root/$plan_dir" + +if [[ ! -d "$features_dir" ]]; then + printf 'ERROR: missing contract plan directory: %s\n' "$features_dir" >&2 + exit 1 +fi + +if [[ ${#FRAME_CONTRACT_REQUIRED_SECTIONS[@]} -eq 0 ]]; then + printf 'ERROR: contract policy must define FRAME_CONTRACT_REQUIRED_SECTIONS\n' >&2 + exit 1 +fi + +if [[ ${#FRAME_CONTRACT_EVIDENCE_LANES[@]} -eq 0 ]]; then + printf 'ERROR: contract policy must define FRAME_CONTRACT_EVIDENCE_LANES\n' >&2 + exit 1 +fi + +status=0 +readonly array_sep=$'\034' + +join_by_sep() { + local sep="$1" + shift + local joined="" + local item + + for item in "$@"; do + if [[ -n "$joined" ]]; then + joined+="$sep" + fi + joined+="$item" + done + + printf '%s' "$joined" +} + +is_substantive_path() { + local path="$1" + local pattern + + for pattern in "${FRAME_CONTRACT_SUBSTANTIVE_PATH_PATTERNS[@]:-}"; do + case "$path" in + $pattern) + return 0 + ;; + esac + done + + for pattern in "${FRAME_CONTRACT_SUBSTANTIVE_TOP_LEVEL_FILES[@]:-}"; do + if [[ "$path" == "$pattern" ]]; then + return 0 + fi + done + + return 1 +} + +collect_changed_files() { + if [[ -n "${FRAME_CHANGE_CONTRACT_CHANGED_FILES:-}" ]]; then + printf '%s\n' "${FRAME_CHANGE_CONTRACT_CHANGED_FILES}" + return + fi + + if ! command -v git >/dev/null 2>&1 || ! git -C "$repo_root" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + return + fi + + if [[ -n "${FRAME_CHANGE_CONTRACT_RANGE:-}" ]]; then + git -C "$repo_root" diff --name-only --diff-filter=ACMR "${FRAME_CHANGE_CONTRACT_RANGE}" || true + return + fi + + if git -C "$repo_root" rev-parse --verify HEAD >/dev/null 2>&1; then + git -C "$repo_root" diff --name-only --diff-filter=ACMR HEAD -- || true + fi + git -C "$repo_root" ls-files --others --exclude-standard || true +} + +validate_feature_file() { + local feature_file="$1" + local feature_state_dir + local required_sections_raw lifecycle_values_raw uncertainty_values_raw + local evidence_status_values_raw yes_no_values_raw implementation_status_values_raw + local verification_status_values_raw evidence_lanes_raw + + feature_state_dir="$(basename "$(dirname "$feature_file")")" + + required_sections_raw="$(join_by_sep "$array_sep" "${FRAME_CONTRACT_REQUIRED_SECTIONS[@]}")" + lifecycle_values_raw="$(join_by_sep "$array_sep" "${FRAME_CONTRACT_LIFECYCLE_VALUES[@]}")" + uncertainty_values_raw="$(join_by_sep "$array_sep" "${FRAME_CONTRACT_UNCERTAINTY_VALUES[@]}")" + evidence_status_values_raw="$(join_by_sep "$array_sep" "${FRAME_CONTRACT_EVIDENCE_STATUS_VALUES[@]}")" + yes_no_values_raw="$(join_by_sep "$array_sep" "${FRAME_CONTRACT_YES_NO_VALUES[@]}")" + implementation_status_values_raw="$(join_by_sep "$array_sep" "${FRAME_CONTRACT_IMPLEMENTATION_STATUS_VALUES[@]}")" + verification_status_values_raw="$(join_by_sep "$array_sep" "${FRAME_CONTRACT_VERIFICATION_STATUS_VALUES[@]}")" + evidence_lanes_raw="$(join_by_sep "$array_sep" "${FRAME_CONTRACT_EVIDENCE_LANES[@]}")" + + awk \ + -v sep="$array_sep" \ + -v required_sections_raw="$required_sections_raw" \ + -v lifecycle_values_raw="$lifecycle_values_raw" \ + -v uncertainty_values_raw="$uncertainty_values_raw" \ + -v evidence_status_values_raw="$evidence_status_values_raw" \ + -v yes_no_values_raw="$yes_no_values_raw" \ + -v implementation_status_values_raw="$implementation_status_values_raw" \ + -v verification_status_values_raw="$verification_status_values_raw" \ + -v evidence_lanes_raw="$evidence_lanes_raw" \ + -v feature_state_dir="$feature_state_dir" \ + ' + BEGIN { + status = 0 + split(required_sections_raw, required_sections_list, sep) + split(lifecycle_values_raw, lifecycle_values_list, sep) + split(uncertainty_values_raw, uncertainty_values_list, sep) + split(evidence_status_values_raw, evidence_status_values_list, sep) + split(yes_no_values_raw, yes_no_values_list, sep) + split(implementation_status_values_raw, implementation_status_values_list, sep) + split(verification_status_values_raw, verification_status_values_list, sep) + split(evidence_lanes_raw, evidence_lanes_list, sep) + + for (i in required_sections_list) { + if (required_sections_list[i] != "") { + required_sections[required_sections_list[i]] = 1 + } + } + for (i in lifecycle_values_list) { + if (lifecycle_values_list[i] != "") { + allowed_lifecycle[lifecycle_values_list[i]] = 1 + } + } + for (i in uncertainty_values_list) { + if (uncertainty_values_list[i] != "") { + allowed_band[uncertainty_values_list[i]] = 1 + } + } + for (i in evidence_status_values_list) { + if (evidence_status_values_list[i] != "") { + allowed_status[evidence_status_values_list[i]] = 1 + } + } + for (i in yes_no_values_list) { + if (yes_no_values_list[i] != "") { + allowed_yes_no[yes_no_values_list[i]] = 1 + } + } + for (i in implementation_status_values_list) { + if (implementation_status_values_list[i] != "") { + allowed_impl_status[implementation_status_values_list[i]] = 1 + } + } + for (i in verification_status_values_list) { + if (verification_status_values_list[i] != "") { + allowed_verification_status[verification_status_values_list[i]] = 1 + } + } + for (i in evidence_lanes_list) { + if (evidence_lanes_list[i] != "") { + expected_lane[evidence_lanes_list[i]] = 1 + } + } + } + + function trim(value) { + sub(/^[[:space:]]+/, "", value) + sub(/[[:space:]]+$/, "", value) + return value + } + + function parse_bullet(prefix, target) { + value = $0 + sub("^- " prefix ": ", "", value) + target = trim(value) + return target + } + + function fail(message) { + printf("%s: %s\n", FILENAME, message) + status = 1 + } + + function parse_matrix_line(raw, clean, parts, lane_name, i, field, pair, eq_pos, key, val) { + clean = raw + sub(/^- /, "", clean) + split(clean, parts, /\|/) + lane_name = trim(parts[1]) + + if (!(lane_name in expected_lane)) { + fail("Evidence Matrix has unknown lane \"" lane_name "\"") + return + } + seen_lane[lane_name] = 1 + + for (i = 2; i <= length(parts); ++i) { + field = trim(parts[i]) + eq_pos = index(field, "=") + if (eq_pos == 0) { + fail("Evidence Matrix entry for \"" lane_name "\" must use key=value fields") + continue + } + key = trim(substr(field, 1, eq_pos - 1)) + val = trim(substr(field, eq_pos + 1)) + evidence[lane_name, key] = val + } + } + + /^## / { + current_section = $0 + seen_section[current_section] = 1 + next + } + + current_section == "## Lifecycle" { + if ($0 ~ /^- State: /) { + lifecycle_state = parse_bullet("State", lifecycle_state) + } else if ($0 ~ /^- Supersedes: /) { + lifecycle_supersedes = parse_bullet("Supersedes", lifecycle_supersedes) + } else if ($0 ~ /^- Superseded by: /) { + lifecycle_superseded_by = parse_bullet("Superseded by", lifecycle_superseded_by) + } + next + } + + current_section == "## Contract" { + if ($0 ~ /^- Must remain true: /) { + contract_must_remain = parse_bullet("Must remain true", contract_must_remain) + } else if ($0 ~ /^- Must become true: /) { + contract_must_become = parse_bullet("Must become true", contract_must_become) + } else if ($0 ~ /^- Success signals: /) { + contract_success_signals = parse_bullet("Success signals", contract_success_signals) + } + next + } + + current_section == "## Uncertainty And Cost" { + if ($0 ~ /^- Product uncertainty: /) { + product_uncertainty = parse_bullet("Product uncertainty", product_uncertainty) + } else if ($0 ~ /^- Technical uncertainty: /) { + technical_uncertainty = parse_bullet("Technical uncertainty", technical_uncertainty) + } else if ($0 ~ /^- Implementation cost: /) { + implementation_cost = parse_bullet("Implementation cost", implementation_cost) + } else if ($0 ~ /^- Validation cost: /) { + validation_cost = parse_bullet("Validation cost", validation_cost) + } else if ($0 ~ /^- Notes: /) { + uncertainty_notes = parse_bullet("Notes", uncertainty_notes) + } + next + } + + current_section == "## Responsibilities" { + if ($0 ~ /^- Implementer: /) { + implementer = parse_bullet("Implementer", implementer) + } else if ($0 ~ /^- Verifier: /) { + verifier = parse_bullet("Verifier", verifier) + } else if ($0 ~ /^- Approver: /) { + approver = parse_bullet("Approver", approver) + } + next + } + + current_section == "## Evidence Matrix" { + if ($0 ~ /^- /) { + parse_matrix_line($0) + } + next + } + + current_section == "## Implementation Notes" { + if ($0 ~ /^- Owner: /) { + implementation_owner = parse_bullet("Owner", implementation_owner) + } else if ($0 ~ /^- Status: /) { + implementation_status = parse_bullet("Status", implementation_status) + } else if ($0 ~ /^- Notes: /) { + implementation_notes = parse_bullet("Notes", implementation_notes) + } + next + } + + current_section == "## Verification Notes" { + if ($0 ~ /^- Owner: /) { + verification_owner = parse_bullet("Owner", verification_owner) + } else if ($0 ~ /^- Status: /) { + verification_status = parse_bullet("Status", verification_status) + } else if ($0 ~ /^- Commands: /) { + verification_commands = parse_bullet("Commands", verification_commands) + } else if ($0 ~ /^- Observed result: /) { + verification_observed_result = parse_bullet("Observed result", verification_observed_result) + } else if ($0 ~ /^- Contract mismatches: /) { + verification_contract_mismatches = parse_bullet("Contract mismatches", verification_contract_mismatches) + } + next + } + + current_section == "## Waivers" { + if ($0 ~ /^- Self-validation rationale: /) { + waiver_self_validation = parse_bullet("Self-validation rationale", waiver_self_validation) + } + next + } + + END { + for (section in required_sections) { + if (!(section in seen_section)) { + fail("missing required section " section) + } + } + + if (!(lifecycle_state in allowed_lifecycle)) { + fail("Lifecycle state must be one of: planned, active, superseded, done") + } + if (trim(lifecycle_supersedes) == "") { + fail("Lifecycle must define Supersedes") + } + if (trim(lifecycle_superseded_by) == "") { + fail("Lifecycle must define Superseded by") + } + if (lifecycle_state == "superseded" && lifecycle_superseded_by == "none") { + fail("Superseded lifecycle state requires a \"Superseded by\" reference") + } + if (feature_state_dir != lifecycle_state) { + fail("Feature record directory must match Lifecycle state") + } + + if (trim(contract_must_remain) == "") { + fail("Contract must define \"Must remain true\"") + } + if (trim(contract_must_become) == "") { + fail("Contract must define \"Must become true\"") + } + if (trim(contract_success_signals) == "") { + fail("Contract must define \"Success signals\"") + } + + if (!(product_uncertainty in allowed_band)) { + fail("Product uncertainty must be one of: low, medium, high") + } + if (!(technical_uncertainty in allowed_band)) { + fail("Technical uncertainty must be one of: low, medium, high") + } + if (!(implementation_cost in allowed_band)) { + fail("Implementation cost must be one of: low, medium, high") + } + if (!(validation_cost in allowed_band)) { + fail("Validation cost must be one of: low, medium, high") + } + if (trim(uncertainty_notes) == "") { + fail("Uncertainty And Cost must include Notes") + } + + if (trim(implementer) == "") { + fail("Responsibilities must define Implementer") + } + if (trim(verifier) == "") { + fail("Responsibilities must define Verifier") + } + + for (lane in expected_lane) { + if (!(lane in seen_lane)) { + fail("Evidence Matrix must include lane \"" lane "\"") + continue + } + + impact = evidence[lane, "impact"] + evidence_status = evidence[lane, "status"] + rationale = evidence[lane, "rationale"] + verifier_note = evidence[lane, "verifier_note"] + + if (!(impact in allowed_yes_no)) { + fail(lane " evidence impact must be one of: yes, no") + } + if (!(evidence_status in allowed_status)) { + fail(lane " evidence status must be one of: passed, waived, not_applicable, missing") + } + if (evidence_status == "missing") { + fail(lane " evidence must not be marked missing") + } + if (impact == "yes" && evidence_status == "not_applicable") { + fail(lane " evidence cannot be not_applicable when impact is yes") + } + if (trim(rationale) == "") { + fail(lane " evidence rationale must be explicit") + } + if (evidence_status == "waived" && rationale == "none") { + fail(lane " evidence is waived but rationale is none") + } + if (trim(verifier_note) == "") { + fail(lane " evidence must include verifier_note") + } + } + + if (trim(implementation_owner) == "") { + fail("Implementation Notes must define Owner") + } + if (implementation_owner != implementer) { + fail("Implementation Notes owner must match Responsibilities implementer") + } + if (!(implementation_status in allowed_impl_status)) { + fail("Implementation Notes status must be one of: planned, in_progress, completed") + } + if (trim(implementation_notes) == "") { + fail("Implementation Notes must include Notes") + } + + if (trim(verification_owner) == "") { + fail("Verification Notes must define Owner") + } + if (verification_owner != verifier) { + fail("Verification Notes owner must match Responsibilities verifier") + } + if (!(verification_status in allowed_verification_status)) { + fail("Verification Notes status must be one of: pending, in_progress, completed") + } + if (trim(verification_commands) == "") { + fail("Verification Notes must include Commands") + } + if (trim(verification_observed_result) == "") { + fail("Verification Notes must include Observed result") + } + if (trim(verification_contract_mismatches) == "") { + fail("Verification Notes must include Contract mismatches") + } + + if (trim(waiver_self_validation) == "") { + fail("Waivers must define Self-validation rationale") + } + + if (implementer == verifier) { + if (waiver_self_validation == "none") { + fail("Implementer and Verifier match without a self-validation rationale") + } + if (!(product_uncertainty == "low" && technical_uncertainty == "low" && + implementation_cost != "high" && validation_cost != "high")) { + if (trim(approver) == "") { + fail("Higher-risk self-validation requires an Approver") + } + if (verification_status != "completed") { + fail("Higher-risk self-validation requires completed verification status") + } + } + } + + exit status + } + ' "$feature_file" +} + +changed_files="$(collect_changed_files)" +substantive_change_count=0 +changed_plan_count=0 +substantive_paths="" + +if [[ -n "$changed_files" ]]; then + while IFS= read -r changed_file; do + [[ -n "$changed_file" ]] || continue + + if is_substantive_path "$changed_file"; then + substantive_change_count=$((substantive_change_count + 1)) + substantive_paths+="$changed_file"$'\n' + fi + + case "$changed_file" in + "$plan_dir"/*.md|"$plan_dir"/*/*.md) + if [[ "$(basename "$changed_file")" != "$template_basename" && + "$(basename "$changed_file")" != "README.md" ]]; then + changed_plan_count=$((changed_plan_count + 1)) + fi + ;; + esac + done <<<"$changed_files" +fi + +if ((substantive_change_count > 0 && changed_plan_count == 0)); then + printf 'ERROR: substantive repo-owned changes require a non-template feature_records plan update.\n' >&2 + printf 'Substantive changed paths:\n%s' "$substantive_paths" >&2 + status=1 +fi + +shopt -s nullglob +feature_files=("$features_dir"/*/*.md) +shopt -u nullglob + +for feature_file in "${feature_files[@]}"; do + if ! validate_feature_file "$feature_file"; then + status=1 + fi +done + +exit "$status" diff --git a/scripts/check-release-hygiene.sh b/scripts/check-release-hygiene.sh index 9ae9497..fc39940 100755 --- a/scripts/check-release-hygiene.sh +++ b/scripts/check-release-hygiene.sh @@ -91,6 +91,9 @@ print_violation "repository must not track binary release artifacts" "$tracked_b commentary_check_output="$(bash "$repo_root/scripts/check-test-commentary.sh" "$repo_root" 2>&1 || true)" print_violation "test sources must keep WHAT/HOW/WHY commentary blocks" "$commentary_check_output" +change_contract_output="$(bash "$repo_root/scripts/check-change-contracts.sh" "$repo_root" 2>&1 || true)" +print_violation "feature plans must keep explicit lifecycle, contract, ownership, evidence, and verifier fields" "$change_contract_output" + if ((failures != 0)); then exit 1 fi diff --git a/scripts/init-project.sh b/scripts/init-project.sh index c2cbd32..4fd7014 100755 --- a/scripts/init-project.sh +++ b/scripts/init-project.sh @@ -83,6 +83,8 @@ do_replace() { -e "s/cpp_agentic_development_frame/${SNAKE_NAME}/g" \ -e "s/ProjectCoreTest/${PASCAL_NAME}CoreTest/g" \ -e "s/ProjectCore/${PASCAL_NAME}Core/g" \ + -e "s/namespace frame::test/namespace ${PREFIX_LOWER}::test/g" \ + -e "s/namespace frame/namespace ${PREFIX_LOWER}/g" \ -e "s/projectcore/${PREFIX_LOWER}core/g" \ -e "s/project_core/${PREFIX_LOWER}_core/g" \ -e "s/frame_cli/${PREFIX_LOWER}_cli/g" \ @@ -97,6 +99,7 @@ do_replace() { do_rename_file() { local old_path="$REPO_ROOT/$1" + [[ -f "$old_path" ]] || return 0 local dir=$(dirname "$old_path") local old_name=$(basename "$old_path") local new_name=$(echo "$old_name" \ @@ -144,6 +147,7 @@ else trap 'rm -rf "$TMPDIR"' EXIT for file in $TRACKED_FILES; do + [[ -f "$REPO_ROOT/$file" ]] || continue mkdir -p "$TMPDIR/$(dirname "$file")" cp "$REPO_ROOT/$file" "$TMPDIR/$file" done @@ -155,6 +159,8 @@ else -e "s/cpp_agentic_development_frame/${SNAKE_NAME}/g" \ -e "s/ProjectCoreTest/${PASCAL_NAME}CoreTest/g" \ -e "s/ProjectCore/${PASCAL_NAME}Core/g" \ + -e "s/namespace frame::test/namespace ${PREFIX_LOWER}::test/g" \ + -e "s/namespace frame/namespace ${PREFIX_LOWER}/g" \ -e "s/projectcore/${PREFIX_LOWER}core/g" \ -e "s/project_core/${PREFIX_LOWER}_core/g" \ -e "s/frame_cli/${PREFIX_LOWER}_cli/g" \ diff --git a/scripts/run-release-checklist.sh b/scripts/run-release-checklist.sh index bcb84ab..b1bc8b6 100755 --- a/scripts/run-release-checklist.sh +++ b/scripts/run-release-checklist.sh @@ -66,6 +66,7 @@ fi note "Release checklist build directory: $build_dir" run_cmd bash scripts/check-release-hygiene.sh +run_cmd bash scripts/check-change-contracts.sh run_cmd cmake -S . -B "$build_dir" "${generator_args[@]}" -DCMAKE_BUILD_TYPE=Debug "${extra_cmake_args[@]}" run_build_target "$build_dir" cmake --build "$build_dir" -j"$(nproc)" run_cmd ctest --test-dir "$build_dir" --output-on-failure diff --git a/scripts/set-feature-record-lifecycle.sh b/scripts/set-feature-record-lifecycle.sh new file mode 100644 index 0000000..b21becf --- /dev/null +++ b/scripts/set-feature-record-lifecycle.sh @@ -0,0 +1,180 @@ +#!/usr/bin/env bash + +set -euo pipefail + +usage() { + cat <<'EOF_USAGE' +Usage: bash scripts/set-feature-record-lifecycle.sh [--repo-root DIR] [--superseded-by PATH] + +Moves a feature record into the lifecycle directory that matches +and updates the record's `## Lifecycle` state fields accordingly. + +Examples: + bash scripts/set-feature-record-lifecycle.sh feature_records/planned/example.md active + bash scripts/set-feature-record-lifecycle.sh \ + --superseded-by feature_records/done/replacement.md \ + feature_records/active/old-plan.md superseded +EOF_USAGE +} + +die() { + printf 'ERROR: %s\n' "$*" >&2 + exit 1 +} + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +superseded_by="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --repo-root) + [[ $# -ge 2 ]] || die "--repo-root requires a value" + repo_root="$2" + shift 2 + ;; + --superseded-by) + [[ $# -ge 2 ]] || die "--superseded-by requires a value" + superseded_by="$2" + shift 2 + ;; + --help) + usage + exit 0 + ;; + --*) + die "Unknown option: $1" + ;; + *) + break + ;; + esac +done + +[[ $# -eq 2 ]] || die "Expected " + +record_arg="$1" +target_state="$2" +policy_file="$repo_root/config/change-contract-policy.sh" + +[[ -f "$policy_file" ]] || die "Missing contract policy file: $policy_file" +source "$policy_file" + +plan_dir="${FRAME_CONTRACT_PLAN_DIR:-feature_records}" +plan_root="$repo_root/$plan_dir" + +case "$target_state" in +planned|active|done|superseded) + ;; +*) + die "Target state must be one of: planned, active, done, superseded" + ;; +esac + +case "$record_arg" in +/*) + record_path="$record_arg" + ;; +*) + record_path="$repo_root/$record_arg" + ;; +esac + +[[ -f "$record_path" ]] || die "Feature record does not exist: $record_path" + +case "$record_path" in +"$plan_root"/*) + ;; +*) + die "Feature record must live under $plan_root" + ;; +esac + +record_basename="$(basename "$record_path")" +[[ "$record_basename" != "$FRAME_CONTRACT_TEMPLATE_BASENAME" ]] || die "Template cannot be transitioned" +[[ "$record_basename" != "README.md" ]] || die "feature_records/README.md cannot be transitioned" + +target_dir="$plan_root/$target_state" +mkdir -p "$target_dir" +target_path="$target_dir/$record_basename" + +if [[ "$target_state" == "superseded" ]]; then + [[ -n "$superseded_by" ]] || die "--superseded-by is required when moving to superseded" + case "$superseded_by" in + /*) + superseded_by_path="$superseded_by" + ;; + *) + superseded_by_path="$repo_root/$superseded_by" + ;; + esac + + [[ -f "$superseded_by_path" ]] || die "Replacement record does not exist: $superseded_by_path" + case "$superseded_by_path" in + "$plan_root"/*) + ;; + *) + die "Replacement record must live under $plan_root" + ;; + esac + superseded_by_value="${superseded_by_path#"$repo_root"/}" +else + superseded_by_value="none" +fi + +temp_file="$(mktemp)" +trap 'rm -f "$temp_file"' EXIT + +awk \ + -v target_state="$target_state" \ + -v superseded_by_value="$superseded_by_value" \ + ' + BEGIN { + in_lifecycle = 0 + saw_state = 0 + saw_superseded_by = 0 + } + + /^## / { + if (in_lifecycle && !saw_state) { + print "- State: " target_state + } + if (in_lifecycle && !saw_superseded_by) { + print "- Superseded by: " superseded_by_value + } + in_lifecycle = ($0 == "## Lifecycle") + print + next + } + + { + if (in_lifecycle && $0 ~ /^- State: /) { + print "- State: " target_state + saw_state = 1 + next + } + if (in_lifecycle && $0 ~ /^- Superseded by: /) { + print "- Superseded by: " superseded_by_value + saw_superseded_by = 1 + next + } + print + } + + END { + if (in_lifecycle && !saw_state) { + print "- State: " target_state + } + if (in_lifecycle && !saw_superseded_by) { + print "- Superseded by: " superseded_by_value + } + } + ' "$record_path" >"$temp_file" + +mv "$temp_file" "$record_path" +trap - EXIT + +if [[ "$record_path" != "$target_path" ]]; then + mv "$record_path" "$target_path" +fi + +printf 'Updated feature record lifecycle: %s -> %s\n' "${record_arg}" "${target_path#"$repo_root"/}" diff --git a/scripts/test-change-contracts.sh b/scripts/test-change-contracts.sh new file mode 100644 index 0000000..c53a434 --- /dev/null +++ b/scripts/test-change-contracts.sh @@ -0,0 +1,107 @@ +#!/usr/bin/env bash + +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +fixtures_dir="$repo_root/tests/fixtures/change_contracts" +checker="$repo_root/scripts/check-change-contracts.sh" +policy_source="$repo_root/config/change-contract-policy.sh" + +source "$policy_source" + +run_case() { + local fixture_name="$1" + local expected_result="$2" + local changed_files="${3:-$FRAME_CONTRACT_PLAN_DIR/active/$fixture_name.md}" + local feature_dir="${4:-active}" + local temp_root + local actual_result + + temp_root="$(mktemp -d /tmp/frame-change-contract-XXXXXX)" + mkdir -p \ + "$temp_root/config" \ + "$temp_root/$FRAME_CONTRACT_PLAN_DIR/active" \ + "$temp_root/$FRAME_CONTRACT_PLAN_DIR/planned" \ + "$temp_root/$FRAME_CONTRACT_PLAN_DIR/done" \ + "$temp_root/$FRAME_CONTRACT_PLAN_DIR/superseded" + cp "$policy_source" "$temp_root/config/change-contract-policy.sh" + cp "$repo_root/$FRAME_CONTRACT_PLAN_DIR/$FRAME_CONTRACT_TEMPLATE_BASENAME" \ + "$temp_root/$FRAME_CONTRACT_PLAN_DIR/$FRAME_CONTRACT_TEMPLATE_BASENAME" + cp "$repo_root/$FRAME_CONTRACT_PLAN_DIR/README.md" \ + "$temp_root/$FRAME_CONTRACT_PLAN_DIR/README.md" + cp "$fixtures_dir/$fixture_name.md" \ + "$temp_root/$FRAME_CONTRACT_PLAN_DIR/$feature_dir/$fixture_name.md" + + if FRAME_CHANGE_CONTRACT_CHANGED_FILES="$changed_files" bash "$checker" "$temp_root" >/dev/null 2>&1; then + actual_result="pass" + else + actual_result="fail" + fi + + rm -rf "$temp_root" + + if [[ "$actual_result" != "$expected_result" ]]; then + printf 'Fixture %s expected %s but got %s\n' "$fixture_name" "$expected_result" "$actual_result" >&2 + exit 1 + fi +} + +run_case_with_policy_override() { + local fixture_name="$1" + local expected_result="$2" + local changed_files="$3" + local temp_root + local actual_result + + temp_root="$(mktemp -d /tmp/frame-change-contract-XXXXXX)" + mkdir -p "$temp_root/config" "$temp_root/docs/plans/active" + cp "$fixtures_dir/$fixture_name.md" "$temp_root/docs/plans/active/$fixture_name.md" + apply_override_policy "$temp_root/config/change-contract-policy.sh" + cp "$repo_root/$FRAME_CONTRACT_PLAN_DIR/$FRAME_CONTRACT_TEMPLATE_BASENAME" \ + "$temp_root/docs/plans/TEMPLATE.md" + cp "$repo_root/$FRAME_CONTRACT_PLAN_DIR/README.md" \ + "$temp_root/docs/plans/README.md" + + if FRAME_CHANGE_CONTRACT_CHANGED_FILES="$changed_files" bash "$checker" "$temp_root" >/dev/null 2>&1; then + actual_result="pass" + else + actual_result="fail" + fi + + rm -rf "$temp_root" + + if [[ "$actual_result" != "$expected_result" ]]; then + printf 'Policy override fixture %s expected %s but got %s\n' \ + "$fixture_name" "$expected_result" "$actual_result" >&2 + exit 1 + fi +} + +apply_override_policy() { + local policy_target="$1" + + cp "$policy_source" "$policy_target" + perl -0pi -e "s/FRAME_CONTRACT_PLAN_DIR=\"feature_records\"/FRAME_CONTRACT_PLAN_DIR=\"docs\\/plans\"/" "$policy_target" + perl -0pi -e "s/'src\\/\\*'/'lib\\/*'/" "$policy_target" + perl -0pi -e "s/'tests\\/\\*'/'guide\\/*'/" "$policy_target" + perl -0pi -e "s/'CMakeLists.txt'/'BUILDING.md'/" "$policy_target" +} + +run_case valid_contract pass +run_case missing_contract_section fail +run_case invalid_uncertainty fail +run_case missing_verifier fail +run_case self_validation_without_waiver fail +run_case self_validation_with_waiver pass +run_case high_risk_self_validation_requires_approver fail +run_case waived_without_rationale fail +run_case missing_evidence_state fail +run_case impact_requires_evidence fail +run_case missing_verification_commands fail +run_case superseded_requires_pointer fail +run_case missing_plan_for_substantive_change fail $'src/projectcore.cpp' +run_case mismatched_directory_state fail $'feature_records/planned/mismatched_directory_state.md' planned +run_case_with_policy_override valid_contract pass $'docs/plans/active/valid_contract.md' +run_case_with_policy_override valid_contract fail $'lib/projectcore.cpp' + +printf 'Change contract fixtures passed.\n' diff --git a/scripts/test-feature-record-lifecycle.sh b/scripts/test-feature-record-lifecycle.sh new file mode 100644 index 0000000..1cc512f --- /dev/null +++ b/scripts/test-feature-record-lifecycle.sh @@ -0,0 +1,199 @@ +#!/usr/bin/env bash + +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +helper="$repo_root/scripts/set-feature-record-lifecycle.sh" +checker="$repo_root/scripts/check-change-contracts.sh" + +die() { + printf 'ERROR: %s\n' "$*" >&2 + exit 1 +} + +assert_exists() { + local path="$1" + [[ -f "$path" ]] || die "Expected file to exist: $path" +} + +assert_missing() { + local path="$1" + [[ ! -e "$path" ]] || die "Expected path to be absent: $path" +} + +assert_contains() { + local path="$1" + local pattern="$2" + if ! grep -Fq -- "$pattern" "$path"; then + die "Expected $path to contain: $pattern" + fi +} + +make_temp_repo() { + local temp_root + temp_root="$(mktemp -d /tmp/frame-feature-record-lifecycle-XXXXXX)" + mkdir -p \ + "$temp_root/config" \ + "$temp_root/scripts" \ + "$temp_root/feature_records/planned" \ + "$temp_root/feature_records/active" \ + "$temp_root/feature_records/done" \ + "$temp_root/feature_records/superseded" + cp "$repo_root/config/change-contract-policy.sh" "$temp_root/config/change-contract-policy.sh" + cp "$repo_root/feature_records/TEMPLATE.md" "$temp_root/feature_records/TEMPLATE.md" + cp "$repo_root/feature_records/README.md" "$temp_root/feature_records/README.md" + printf '%s\n' "$temp_root" +} + +write_record() { + local path="$1" + local state="$2" + + cat >"$path" </dev/null + + assert_missing "$record_path" + assert_exists "$temp_root/feature_records/active/test-record.md" + assert_contains "$temp_root/feature_records/active/test-record.md" "- State: active" + bash "$checker" "$temp_root" >/dev/null + rm -rf "$temp_root" +} + +test_move_to_done() { + local temp_root record_path + temp_root="$(make_temp_repo)" + record_path="$temp_root/feature_records/active/test-record.md" + write_record "$record_path" active + + bash "$helper" --repo-root "$temp_root" feature_records/active/test-record.md done >/dev/null + + assert_missing "$record_path" + assert_exists "$temp_root/feature_records/done/test-record.md" + assert_contains "$temp_root/feature_records/done/test-record.md" "- State: done" + bash "$checker" "$temp_root" >/dev/null + rm -rf "$temp_root" +} + +test_move_to_superseded() { + local temp_root record_path replacement_path + temp_root="$(make_temp_repo)" + record_path="$temp_root/feature_records/active/test-record.md" + replacement_path="$temp_root/feature_records/done/replacement.md" + write_record "$record_path" active + write_record "$replacement_path" done + + bash "$helper" \ + --repo-root "$temp_root" \ + --superseded-by feature_records/done/replacement.md \ + feature_records/active/test-record.md superseded >/dev/null + + assert_missing "$record_path" + assert_exists "$temp_root/feature_records/superseded/test-record.md" + assert_contains "$temp_root/feature_records/superseded/test-record.md" "- State: superseded" + assert_contains "$temp_root/feature_records/superseded/test-record.md" "- Superseded by: feature_records/done/replacement.md" + bash "$checker" "$temp_root" >/dev/null + rm -rf "$temp_root" +} + +test_superseded_requires_replacement() { + local temp_root record_path + temp_root="$(make_temp_repo)" + record_path="$temp_root/feature_records/active/test-record.md" + write_record "$record_path" active + + if bash "$helper" --repo-root "$temp_root" feature_records/active/test-record.md superseded >/dev/null 2>&1; then + die "Expected superseded transition without replacement to fail" + fi + + assert_exists "$record_path" + rm -rf "$temp_root" +} + +test_move_to_active +test_move_to_done +test_move_to_superseded +test_superseded_requires_replacement + +printf 'Feature record lifecycle helper tests passed.\n' diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index eea68e8..521a44f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -23,3 +23,11 @@ add_test(NAME frameclihelp COMMAND $ --help) add_test(NAME testcommentarycheck COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/../scripts/check-test-commentary.sh ${CMAKE_CURRENT_SOURCE_DIR}/.. ) + +add_test(NAME changecontractcheck + COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/../scripts/test-change-contracts.sh +) + +add_test(NAME featurerecordlifecyclecheck + COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/../scripts/test-feature-record-lifecycle.sh +) diff --git a/tests/fixtures/change_contracts/high_risk_self_validation_requires_approver.md b/tests/fixtures/change_contracts/high_risk_self_validation_requires_approver.md new file mode 100644 index 0000000..67f5244 --- /dev/null +++ b/tests/fixtures/change_contracts/high_risk_self_validation_requires_approver.md @@ -0,0 +1,73 @@ +# Feature: High Risk Self Validation Requires Approver + +## Motivation + +Exercise the bounded self-validation rule. + +## Proposed Behavior + +The checker should reject higher-risk self-validation when no approver is recorded. + +## Lifecycle + +- State: active +- Supersedes: none +- Superseded by: none + +## Contract + +- Must remain true: Self-validation stays constrained rather than becoming the default. +- Must become true: Higher-risk self-validation requires stronger governance. +- Success signals: The fixture is rejected by the checker. + +## Uncertainty And Cost + +- Product uncertainty: medium +- Technical uncertainty: high +- Implementation cost: medium +- Validation cost: medium +- Notes: The risk bands intentionally exceed the low-risk self-validation path. + +## Responsibilities + +- Implementer: same-agent +- Verifier: same-agent +- Approver: + +## Evidence Matrix + +- Tests | impact=yes | status=passed | rationale=The checker behavior is under test. | verifier_note=The fixture should fail because higher-risk self-validation lacks an approver. +- Docs | impact=no | status=not_applicable | rationale=Docs are unaffected. | verifier_note=No docs lane applies. +- Analyzers | impact=no | status=not_applicable | rationale=Analyzers are unaffected. | verifier_note=No analyzer lane applies. +- Install validation | impact=no | status=not_applicable | rationale=Install is unaffected. | verifier_note=No install lane applies. +- Release hygiene | impact=yes | status=passed | rationale=The checker participates in hygiene enforcement. | verifier_note=The fixture should fail during contract validation. + +## Implementation Notes + +- Owner: same-agent +- Status: completed +- Notes: The shared ownership is intentional for this failure fixture. + +## Verification Notes + +- Owner: same-agent +- Status: completed +- Commands: `bash scripts/check-change-contracts.sh` +- Observed result: The checker should reject the missing approver for higher-risk self-validation. +- Contract mismatches: none + +## Waivers + +- Self-validation rationale: The actor is intentionally the same to exercise the higher-risk path. + +## Files to Add/Modify + +- `feature_records/...` — fixture only + +## Testing Strategy + +Run the checker and expect failure. + +## Open Questions + +None. diff --git a/tests/fixtures/change_contracts/impact_requires_evidence.md b/tests/fixtures/change_contracts/impact_requires_evidence.md new file mode 100644 index 0000000..d3603d9 --- /dev/null +++ b/tests/fixtures/change_contracts/impact_requires_evidence.md @@ -0,0 +1,73 @@ +# Feature: Impact Requires Evidence + +## Motivation + +Exercise impact-driven evidence validation. + +## Proposed Behavior + +The checker should reject `not_applicable` evidence when the corresponding impact is declared as `yes`. + +## Lifecycle + +- State: active +- Supersedes: none +- Superseded by: none + +## Contract + +- Must remain true: Impact declarations must affect required proof. +- Must become true: Declared impact makes the corresponding evidence lane mandatory. +- Success signals: The fixture is rejected by the checker. + +## Uncertainty And Cost + +- Product uncertainty: low +- Technical uncertainty: low +- Implementation cost: low +- Validation cost: low +- Notes: This fixture only checks the impact-to-evidence rule. + +## Responsibilities + +- Implementer: agent-implementer +- Verifier: agent-verifier +- Approver: repo-maintainer + +## Evidence Matrix + +- Tests | impact=yes | status=passed | rationale=The checker behavior is under test. | verifier_note=The tests lane is not the failure point here. +- Docs | impact=yes | status=not_applicable | rationale=This intentionally violates the impact rule. | verifier_note=The checker should reject the docs lane. +- Analyzers | impact=no | status=not_applicable | rationale=Analyzers are unaffected. | verifier_note=No analyzer lane applies. +- Install validation | impact=no | status=not_applicable | rationale=Install is unaffected. | verifier_note=No install lane applies. +- Release hygiene | impact=yes | status=passed | rationale=The checker participates in hygiene enforcement. | verifier_note=The fixture should fail during contract validation. + +## Implementation Notes + +- Owner: agent-implementer +- Status: planned +- Notes: The docs lane is intentionally inconsistent. + +## Verification Notes + +- Owner: agent-verifier +- Status: pending +- Commands: `bash scripts/check-change-contracts.sh` +- Observed result: The checker should reject the docs evidence state. +- Contract mismatches: none + +## Waivers + +- Self-validation rationale: none + +## Files to Add/Modify + +- `feature_records/...` — fixture only + +## Testing Strategy + +Run the checker and expect failure. + +## Open Questions + +None. diff --git a/tests/fixtures/change_contracts/invalid_uncertainty.md b/tests/fixtures/change_contracts/invalid_uncertainty.md new file mode 100644 index 0000000..889b9dd --- /dev/null +++ b/tests/fixtures/change_contracts/invalid_uncertainty.md @@ -0,0 +1,73 @@ +# Feature: Invalid Uncertainty + +## Motivation + +Exercise enum validation for uncertainty fields. + +## Proposed Behavior + +The checker should reject unsupported uncertainty values. + +## Lifecycle + +- State: active +- Supersedes: none +- Superseded by: none + +## Contract + +- Must remain true: Validation stays deterministic. +- Must become true: Unsupported enum values fail closed. +- Success signals: The fixture is rejected by the checker. + +## Uncertainty And Cost + +- Product uncertainty: unknown +- Technical uncertainty: low +- Implementation cost: low +- Validation cost: low +- Notes: Only the product uncertainty value is intentionally invalid. + +## Responsibilities + +- Implementer: agent-implementer +- Verifier: agent-verifier +- Approver: repo-maintainer + +## Evidence Matrix + +- Tests | impact=yes | status=passed | rationale=The fixture exercises the checker. | verifier_note=The fixture should fail on uncertainty parsing. +- Docs | impact=no | status=not_applicable | rationale=Docs are unaffected. | verifier_note=No docs lane applies. +- Analyzers | impact=no | status=not_applicable | rationale=Analyzers are unaffected. | verifier_note=No analyzer lane applies. +- Install validation | impact=no | status=not_applicable | rationale=Install is unaffected. | verifier_note=No install lane applies. +- Release hygiene | impact=yes | status=passed | rationale=The checker participates in hygiene enforcement. | verifier_note=The fixture should fail during contract validation. + +## Implementation Notes + +- Owner: agent-implementer +- Status: planned +- Notes: The invalid enum value is intentional. + +## Verification Notes + +- Owner: agent-verifier +- Status: pending +- Commands: `bash scripts/check-change-contracts.sh` +- Observed result: The checker should reject the unsupported uncertainty value. +- Contract mismatches: none + +## Waivers + +- Self-validation rationale: none + +## Files to Add/Modify + +- `feature_records/...` — fixture only + +## Testing Strategy + +Run the checker and expect failure. + +## Open Questions + +None. diff --git a/tests/fixtures/change_contracts/mismatched_directory_state.md b/tests/fixtures/change_contracts/mismatched_directory_state.md new file mode 100644 index 0000000..c00faef --- /dev/null +++ b/tests/fixtures/change_contracts/mismatched_directory_state.md @@ -0,0 +1,73 @@ +# Feature: Mismatched Directory State + +## Motivation + +Fixtures should prove that a record's location matches its declared lifecycle. + +## Proposed Behavior + +The checker should reject a record placed in the wrong lifecycle directory. + +## Lifecycle + +- State: active +- Supersedes: none +- Superseded by: none + +## Contract + +- Must remain true: Lifecycle state should be explicit and machine-checkable. +- Must become true: The checker should fail when a file path and lifecycle state disagree. +- Success signals: The fixture is rejected. + +## Uncertainty And Cost + +- Product uncertainty: low +- Technical uncertainty: low +- Implementation cost: low +- Validation cost: low +- Notes: This fixture isolates the directory-versus-state rule. + +## Responsibilities + +- Implementer: fixture-author +- Verifier: fixture-reviewer +- Approver: none + +## Evidence Matrix + +- Tests | impact=yes | status=passed | rationale=Fixture validation is the test surface. | verifier_note=Fixture only. +- Docs | impact=no | status=not_applicable | rationale=Docs are out of scope for this fixture. | verifier_note=Fixture only. +- Analyzers | impact=no | status=not_applicable | rationale=Analyzers are out of scope for this fixture. | verifier_note=Fixture only. +- Install validation | impact=no | status=not_applicable | rationale=Install validation is out of scope for this fixture. | verifier_note=Fixture only. +- Release hygiene | impact=no | status=not_applicable | rationale=Release hygiene is out of scope for this fixture. | verifier_note=Fixture only. + +## Implementation Notes + +- Owner: fixture-author +- Status: completed +- Notes: Fixture only. + +## Verification Notes + +- Owner: fixture-reviewer +- Status: completed +- Commands: fixture only +- Observed result: Should fail because the record is intended to be placed under `planned/` while declaring `active`. +- Contract mismatches: none + +## Waivers + +- Self-validation rationale: none + +## Files to Add/Modify + +- `feature_records/...` — fixture only + +## Testing Strategy + +Run the checker fixture suite. + +## Open Questions + +None. diff --git a/tests/fixtures/change_contracts/missing_contract_section.md b/tests/fixtures/change_contracts/missing_contract_section.md new file mode 100644 index 0000000..c8a7777 --- /dev/null +++ b/tests/fixtures/change_contracts/missing_contract_section.md @@ -0,0 +1,67 @@ +# Feature: Missing Contract Section + +## Motivation + +Exercise missing-section failure behavior. + +## Proposed Behavior + +The checker should reject plans missing the contract section. + +## Lifecycle + +- State: active +- Supersedes: none +- Superseded by: none + +## Uncertainty And Cost + +- Product uncertainty: low +- Technical uncertainty: low +- Implementation cost: low +- Validation cost: low +- Notes: This fixture exists only to fail predictably. + +## Responsibilities + +- Implementer: agent-implementer +- Verifier: agent-verifier +- Approver: repo-maintainer + +## Evidence Matrix + +- Tests | impact=yes | status=passed | rationale=The fixture exercises the checker. | verifier_note=The checker should reject the missing section. +- Docs | impact=no | status=not_applicable | rationale=Docs are unaffected. | verifier_note=No docs lane applies. +- Analyzers | impact=no | status=not_applicable | rationale=Analyzers are unaffected. | verifier_note=No analyzer lane applies. +- Install validation | impact=no | status=not_applicable | rationale=Install is unaffected. | verifier_note=No install lane applies. +- Release hygiene | impact=yes | status=passed | rationale=The checker participates in hygiene enforcement. | verifier_note=The fixture should fail during contract validation. + +## Implementation Notes + +- Owner: agent-implementer +- Status: planned +- Notes: This fixture exists only to fail predictably. + +## Verification Notes + +- Owner: agent-verifier +- Status: pending +- Commands: `bash scripts/check-change-contracts.sh` +- Observed result: The checker should reject the missing section. +- Contract mismatches: none + +## Waivers + +- Self-validation rationale: none + +## Files to Add/Modify + +- `feature_records/...` — fixture only + +## Testing Strategy + +Run the checker and expect failure. + +## Open Questions + +None. diff --git a/tests/fixtures/change_contracts/missing_evidence_state.md b/tests/fixtures/change_contracts/missing_evidence_state.md new file mode 100644 index 0000000..68d08c7 --- /dev/null +++ b/tests/fixtures/change_contracts/missing_evidence_state.md @@ -0,0 +1,73 @@ +# Feature: Missing Evidence State + +## Motivation + +Exercise the fail-closed evidence state rule. + +## Proposed Behavior + +The checker should reject `missing` evidence states. + +## Lifecycle + +- State: active +- Supersedes: none +- Superseded by: none + +## Contract + +- Must remain true: Evidence requirements stay explicit. +- Must become true: Missing evidence states fail instead of being ignored. +- Success signals: The fixture is rejected by the checker. + +## Uncertainty And Cost + +- Product uncertainty: low +- Technical uncertainty: low +- Implementation cost: low +- Validation cost: low +- Notes: The tests evidence is intentionally marked missing. + +## Responsibilities + +- Implementer: agent-implementer +- Verifier: agent-verifier +- Approver: repo-maintainer + +## Evidence Matrix + +- Tests | impact=yes | status=missing | rationale=This intentionally fails the checker. | verifier_note=The checker should reject the missing status. +- Docs | impact=no | status=not_applicable | rationale=Docs are unaffected. | verifier_note=No docs lane applies. +- Analyzers | impact=no | status=not_applicable | rationale=Analyzers are unaffected. | verifier_note=No analyzer lane applies. +- Install validation | impact=no | status=not_applicable | rationale=Install is unaffected. | verifier_note=No install lane applies. +- Release hygiene | impact=yes | status=passed | rationale=The checker participates in hygiene enforcement. | verifier_note=The fixture should fail during contract validation. + +## Implementation Notes + +- Owner: agent-implementer +- Status: planned +- Notes: The tests status is intentionally invalid. + +## Verification Notes + +- Owner: agent-verifier +- Status: pending +- Commands: `bash scripts/check-change-contracts.sh` +- Observed result: The checker should reject missing evidence. +- Contract mismatches: none + +## Waivers + +- Self-validation rationale: none + +## Files to Add/Modify + +- `feature_records/...` — fixture only + +## Testing Strategy + +Run the checker and expect failure. + +## Open Questions + +None. diff --git a/tests/fixtures/change_contracts/missing_plan_for_substantive_change.md b/tests/fixtures/change_contracts/missing_plan_for_substantive_change.md new file mode 100644 index 0000000..dd970de --- /dev/null +++ b/tests/fixtures/change_contracts/missing_plan_for_substantive_change.md @@ -0,0 +1,73 @@ +# Feature: Missing Plan For Substantive Change + +## Motivation + +Exercise the substantive-change gate. + +## Proposed Behavior + +The checker should reject substantive repo-owned changes that do not update a non-template plan file. + +## Lifecycle + +- State: active +- Supersedes: none +- Superseded by: none + +## Contract + +- Must remain true: Substantive changes require explicit planning. +- Must become true: A repo-owned change without a plan update fails closed. +- Success signals: The checker is invoked with only a substantive changed file and fails. + +## Uncertainty And Cost + +- Product uncertainty: low +- Technical uncertainty: low +- Implementation cost: low +- Validation cost: low +- Notes: This fixture exists only to verify the change-presence gate. + +## Responsibilities + +- Implementer: agent-implementer +- Verifier: agent-verifier +- Approver: repo-maintainer + +## Evidence Matrix + +- Tests | impact=yes | status=passed | rationale=The checker behavior is under test. | verifier_note=The plan-presence gate should fail before normal validation matters. +- Docs | impact=no | status=not_applicable | rationale=Docs are unaffected. | verifier_note=No docs lane applies. +- Analyzers | impact=no | status=not_applicable | rationale=Analyzers are unaffected. | verifier_note=No analyzer lane applies. +- Install validation | impact=no | status=not_applicable | rationale=Install is unaffected. | verifier_note=No install lane applies. +- Release hygiene | impact=yes | status=passed | rationale=The checker participates in hygiene enforcement. | verifier_note=The plan-presence gate should fail before normal validation matters. + +## Implementation Notes + +- Owner: agent-implementer +- Status: planned +- Notes: This plan should not count because the simulated change list omits any plan path. + +## Verification Notes + +- Owner: agent-verifier +- Status: pending +- Commands: `bash scripts/check-change-contracts.sh` +- Observed result: The checker should fail before this file can satisfy the plan-update requirement. +- Contract mismatches: none + +## Waivers + +- Self-validation rationale: none + +## Files to Add/Modify + +- `feature_records/...` — fixture only + +## Testing Strategy + +Run the checker with a changed-files list that includes only `src/projectcore.cpp`. + +## Open Questions + +None. diff --git a/tests/fixtures/change_contracts/missing_verification_commands.md b/tests/fixtures/change_contracts/missing_verification_commands.md new file mode 100644 index 0000000..a8d16d2 --- /dev/null +++ b/tests/fixtures/change_contracts/missing_verification_commands.md @@ -0,0 +1,72 @@ +# Feature: Missing Verification Commands + +## Motivation + +Exercise explicit verifier evidence requirements. + +## Proposed Behavior + +The checker should reject plans that omit verifier commands. + +## Lifecycle + +- State: active +- Supersedes: none +- Superseded by: none + +## Contract + +- Must remain true: Verification stays a first-class artifact rather than an implied step. +- Must become true: Plans without concrete verifier commands fail closed. +- Success signals: The fixture is rejected by the checker. + +## Uncertainty And Cost + +- Product uncertainty: low +- Technical uncertainty: low +- Implementation cost: low +- Validation cost: low +- Notes: This fixture omits verifier commands on purpose. + +## Responsibilities + +- Implementer: agent-implementer +- Verifier: agent-verifier +- Approver: repo-maintainer + +## Evidence Matrix + +- Tests | impact=yes | status=passed | rationale=The checker behavior is under test. | verifier_note=The checker should reject the verification-notes section. +- Docs | impact=no | status=not_applicable | rationale=Docs are unaffected. | verifier_note=No docs lane applies. +- Analyzers | impact=no | status=not_applicable | rationale=Analyzers are unaffected. | verifier_note=No analyzer lane applies. +- Install validation | impact=no | status=not_applicable | rationale=Install is unaffected. | verifier_note=No install lane applies. +- Release hygiene | impact=yes | status=passed | rationale=The checker participates in hygiene enforcement. | verifier_note=The fixture should fail during contract validation. + +## Implementation Notes + +- Owner: agent-implementer +- Status: planned +- Notes: The implementation side is present but verifier commands are omitted. + +## Verification Notes + +- Owner: agent-verifier +- Status: pending +- Observed result: The checker should reject the missing verifier commands. +- Contract mismatches: none + +## Waivers + +- Self-validation rationale: none + +## Files to Add/Modify + +- `feature_records/...` — fixture only + +## Testing Strategy + +Run the checker and expect failure. + +## Open Questions + +None. diff --git a/tests/fixtures/change_contracts/missing_verifier.md b/tests/fixtures/change_contracts/missing_verifier.md new file mode 100644 index 0000000..89db095 --- /dev/null +++ b/tests/fixtures/change_contracts/missing_verifier.md @@ -0,0 +1,73 @@ +# Feature: Missing Verifier + +## Motivation + +Exercise responsibility validation. + +## Proposed Behavior + +The checker should reject plans that omit a verifier. + +## Lifecycle + +- State: active +- Supersedes: none +- Superseded by: none + +## Contract + +- Must remain true: Ownership fields stay explicit. +- Must become true: Missing verifier fields fail closed. +- Success signals: The fixture is rejected by the checker. + +## Uncertainty And Cost + +- Product uncertainty: low +- Technical uncertainty: low +- Implementation cost: low +- Validation cost: low +- Notes: This fixture leaves the verifier field blank. + +## Responsibilities + +- Implementer: agent-implementer +- Verifier: +- Approver: repo-maintainer + +## Evidence Matrix + +- Tests | impact=yes | status=passed | rationale=The fixture exercises the checker. | verifier_note=The checker should reject the missing verifier. +- Docs | impact=no | status=not_applicable | rationale=Docs are unaffected. | verifier_note=No docs lane applies. +- Analyzers | impact=no | status=not_applicable | rationale=Analyzers are unaffected. | verifier_note=No analyzer lane applies. +- Install validation | impact=no | status=not_applicable | rationale=Install is unaffected. | verifier_note=No install lane applies. +- Release hygiene | impact=yes | status=passed | rationale=The checker participates in hygiene enforcement. | verifier_note=The fixture should fail during contract validation. + +## Implementation Notes + +- Owner: agent-implementer +- Status: planned +- Notes: The verifier field is intentionally left blank. + +## Verification Notes + +- Owner: +- Status: pending +- Commands: `bash scripts/check-change-contracts.sh` +- Observed result: The checker should reject the missing verifier. +- Contract mismatches: none + +## Waivers + +- Self-validation rationale: none + +## Files to Add/Modify + +- `feature_records/...` — fixture only + +## Testing Strategy + +Run the checker and expect failure. + +## Open Questions + +None. diff --git a/tests/fixtures/change_contracts/self_validation_with_waiver.md b/tests/fixtures/change_contracts/self_validation_with_waiver.md new file mode 100644 index 0000000..a4ee09c --- /dev/null +++ b/tests/fixtures/change_contracts/self_validation_with_waiver.md @@ -0,0 +1,73 @@ +# Feature: Self Validation With Waiver + +## Motivation + +Exercise the explicit self-validation waiver path. + +## Proposed Behavior + +The checker should allow matching implementer/verifier values when the work stays low risk and the waiver is explicit. + +## Lifecycle + +- State: active +- Supersedes: none +- Superseded by: none + +## Contract + +- Must remain true: Self-validation remains visible to reviewers. +- Must become true: A waiver allows low-risk self-validation to stay explicit instead of silent. +- Success signals: The fixture passes the checker. + +## Uncertainty And Cost + +- Product uncertainty: low +- Technical uncertainty: low +- Implementation cost: low +- Validation cost: low +- Notes: This fixture stays in the explicitly allowed low-risk self-validation lane. + +## Responsibilities + +- Implementer: same-agent +- Verifier: same-agent +- Approver: repo-maintainer + +## Evidence Matrix + +- Tests | impact=yes | status=passed | rationale=The checker behavior is under test. | verifier_note=The fixture should pass under the low-risk waiver path. +- Docs | impact=no | status=not_applicable | rationale=Docs are unaffected. | verifier_note=No docs lane applies. +- Analyzers | impact=no | status=not_applicable | rationale=Analyzers are unaffected. | verifier_note=No analyzer lane applies. +- Install validation | impact=no | status=not_applicable | rationale=Install is unaffected. | verifier_note=No install lane applies. +- Release hygiene | impact=yes | status=passed | rationale=The checker participates in hygiene enforcement. | verifier_note=The fixture should be accepted by the checker. + +## Implementation Notes + +- Owner: same-agent +- Status: completed +- Notes: The implementation and verification roles are intentionally shared here. + +## Verification Notes + +- Owner: same-agent +- Status: completed +- Commands: `bash scripts/check-change-contracts.sh` +- Observed result: The low-risk self-validation waiver is explicit and accepted. +- Contract mismatches: none + +## Waivers + +- Self-validation rationale: Small low-risk change; verification is still recorded explicitly against the frozen contract. + +## Files to Add/Modify + +- `feature_records/...` — fixture only + +## Testing Strategy + +Run the checker and expect success. + +## Open Questions + +None. diff --git a/tests/fixtures/change_contracts/self_validation_without_waiver.md b/tests/fixtures/change_contracts/self_validation_without_waiver.md new file mode 100644 index 0000000..df1b954 --- /dev/null +++ b/tests/fixtures/change_contracts/self_validation_without_waiver.md @@ -0,0 +1,73 @@ +# Feature: Self Validation Without Waiver + +## Motivation + +Exercise the self-validation guardrail. + +## Proposed Behavior + +The checker should reject plans where the implementer and verifier are the same without rationale. + +## Lifecycle + +- State: active +- Supersedes: none +- Superseded by: none + +## Contract + +- Must remain true: Self-validation stays visible rather than silent. +- Must become true: Matching implementer/verifier requires a waiver. +- Success signals: The fixture is rejected by the checker. + +## Uncertainty And Cost + +- Product uncertainty: low +- Technical uncertainty: low +- Implementation cost: low +- Validation cost: low +- Notes: The only invalid field is the missing self-validation rationale. + +## Responsibilities + +- Implementer: same-agent +- Verifier: same-agent +- Approver: repo-maintainer + +## Evidence Matrix + +- Tests | impact=yes | status=passed | rationale=The fixture exercises the checker. | verifier_note=The checker should reject the missing self-validation waiver. +- Docs | impact=no | status=not_applicable | rationale=Docs are unaffected. | verifier_note=No docs lane applies. +- Analyzers | impact=no | status=not_applicable | rationale=Analyzers are unaffected. | verifier_note=No analyzer lane applies. +- Install validation | impact=no | status=not_applicable | rationale=Install is unaffected. | verifier_note=No install lane applies. +- Release hygiene | impact=yes | status=passed | rationale=The checker participates in hygiene enforcement. | verifier_note=The fixture should fail during contract validation. + +## Implementation Notes + +- Owner: same-agent +- Status: planned +- Notes: The owner match is intentional for this failure fixture. + +## Verification Notes + +- Owner: same-agent +- Status: pending +- Commands: `bash scripts/check-change-contracts.sh` +- Observed result: The checker should reject the missing waiver. +- Contract mismatches: none + +## Waivers + +- Self-validation rationale: none + +## Files to Add/Modify + +- `feature_records/...` — fixture only + +## Testing Strategy + +Run the checker and expect failure. + +## Open Questions + +None. diff --git a/tests/fixtures/change_contracts/superseded_requires_pointer.md b/tests/fixtures/change_contracts/superseded_requires_pointer.md new file mode 100644 index 0000000..05caa1a --- /dev/null +++ b/tests/fixtures/change_contracts/superseded_requires_pointer.md @@ -0,0 +1,73 @@ +# Feature: Superseded Requires Pointer + +## Motivation + +Exercise lifecycle validation for superseded plans. + +## Proposed Behavior + +The checker should reject `superseded` plans that do not point to a replacement. + +## Lifecycle + +- State: superseded +- Supersedes: none +- Superseded by: none + +## Contract + +- Must remain true: Lifecycle state should communicate plan status clearly. +- Must become true: Superseded plans require a pointer to the replacement plan. +- Success signals: The fixture is rejected by the checker. + +## Uncertainty And Cost + +- Product uncertainty: low +- Technical uncertainty: low +- Implementation cost: low +- Validation cost: low +- Notes: This fixture only checks lifecycle validation. + +## Responsibilities + +- Implementer: agent-implementer +- Verifier: agent-verifier +- Approver: repo-maintainer + +## Evidence Matrix + +- Tests | impact=yes | status=passed | rationale=The checker behavior is under test. | verifier_note=The fixture should fail on lifecycle validation. +- Docs | impact=no | status=not_applicable | rationale=Docs are unaffected. | verifier_note=No docs lane applies. +- Analyzers | impact=no | status=not_applicable | rationale=Analyzers are unaffected. | verifier_note=No analyzer lane applies. +- Install validation | impact=no | status=not_applicable | rationale=Install is unaffected. | verifier_note=No install lane applies. +- Release hygiene | impact=yes | status=passed | rationale=The checker participates in hygiene enforcement. | verifier_note=The fixture should fail during contract validation. + +## Implementation Notes + +- Owner: agent-implementer +- Status: planned +- Notes: The lifecycle pointer is intentionally missing. + +## Verification Notes + +- Owner: agent-verifier +- Status: pending +- Commands: `bash scripts/check-change-contracts.sh` +- Observed result: The checker should reject the missing replacement pointer. +- Contract mismatches: none + +## Waivers + +- Self-validation rationale: none + +## Files to Add/Modify + +- `feature_records/...` — fixture only + +## Testing Strategy + +Run the checker and expect failure. + +## Open Questions + +None. diff --git a/tests/fixtures/change_contracts/valid_contract.md b/tests/fixtures/change_contracts/valid_contract.md new file mode 100644 index 0000000..13c1aaa --- /dev/null +++ b/tests/fixtures/change_contracts/valid_contract.md @@ -0,0 +1,75 @@ +# Feature: Valid Contract + +## Motivation + +Keep feature planning explicit and enforceable. + +## Proposed Behavior + +Planned changes must declare ownership, risk, lifecycle, and evidence lanes before implementation. + +## Lifecycle + +- State: active +- Supersedes: none +- Superseded by: none + +## Contract + +- Must remain true: The repo stays portable and validates with local scripts. +- Must become true: Feature plans use explicit contract and evidence fields. +- Success signals: The checker passes and required states are visible in review. + +## Uncertainty And Cost + +- Product uncertainty: low +- Technical uncertainty: medium +- Implementation cost: medium +- Validation cost: low +- Notes: The workflow is new, but the enforcement is limited to template structure. + +## Responsibilities + +- Implementer: agent-implementer +- Verifier: agent-verifier +- Approver: repo-maintainer + +## Evidence Matrix + +- Tests | impact=yes | status=passed | rationale=Tests are affected by the workflow change. | verifier_note=Validated through the checker fixture suite. +- Docs | impact=yes | status=passed | rationale=Docs changed with the workflow. | verifier_note=README and AGENTS guidance were reviewed. +- Analyzers | impact=no | status=not_applicable | rationale=No analyzer-sensitive source change was made. | verifier_note=Analyzer lanes stayed out of scope. +- Install validation | impact=no | status=not_applicable | rationale=Install layout did not change. | verifier_note=Install validation was intentionally skipped. +- Release hygiene | impact=yes | status=passed | rationale=Release-facing workflow changed. | verifier_note=Release hygiene and contract checks were executed. + +## Implementation Notes + +- Owner: agent-implementer +- Status: completed +- Notes: The contract schema and checker were updated together. + +## Verification Notes + +- Owner: agent-verifier +- Status: completed +- Commands: `bash scripts/test-change-contracts.sh`; `bash scripts/check-change-contracts.sh` +- Observed result: The fixture suite and checker both passed. +- Contract mismatches: none + +## Waivers + +- Self-validation rationale: none + +## Files to Add/Modify + +- `scripts/...` — change-contract checker and fixture runner +- `feature_records/...` — stricter template contract +- `README.md` — describe the workflow + +## Testing Strategy + +Run the checker on valid and invalid fixtures and include it in release hygiene. + +## Open Questions + +Whether future slices should require evidence truth-checking instead of declaration-only enforcement. diff --git a/tests/fixtures/change_contracts/waived_without_rationale.md b/tests/fixtures/change_contracts/waived_without_rationale.md new file mode 100644 index 0000000..e58c584 --- /dev/null +++ b/tests/fixtures/change_contracts/waived_without_rationale.md @@ -0,0 +1,73 @@ +# Feature: Waived Without Rationale + +## Motivation + +Exercise evidence waiver validation. + +## Proposed Behavior + +The checker should reject waived evidence without rationale. + +## Lifecycle + +- State: active +- Supersedes: none +- Superseded by: none + +## Contract + +- Must remain true: Waivers require explicit justification. +- Must become true: A waived evidence state fails when rationale is missing. +- Success signals: The fixture is rejected by the checker. + +## Uncertainty And Cost + +- Product uncertainty: low +- Technical uncertainty: low +- Implementation cost: low +- Validation cost: low +- Notes: The docs evidence is intentionally waived without a rationale. + +## Responsibilities + +- Implementer: agent-implementer +- Verifier: agent-verifier +- Approver: repo-maintainer + +## Evidence Matrix + +- Tests | impact=yes | status=passed | rationale=The fixture exercises the checker. | verifier_note=The tests lane is not the failure point here. +- Docs | impact=yes | status=waived | rationale=none | verifier_note=The checker should reject the waived docs evidence. +- Analyzers | impact=no | status=not_applicable | rationale=Analyzers are unaffected. | verifier_note=No analyzer lane applies. +- Install validation | impact=no | status=not_applicable | rationale=Install is unaffected. | verifier_note=No install lane applies. +- Release hygiene | impact=yes | status=passed | rationale=The checker participates in hygiene enforcement. | verifier_note=The fixture should fail during contract validation. + +## Implementation Notes + +- Owner: agent-implementer +- Status: planned +- Notes: The docs evidence is intentionally waived without rationale. + +## Verification Notes + +- Owner: agent-verifier +- Status: pending +- Commands: `bash scripts/check-change-contracts.sh` +- Observed result: The checker should reject the waived docs evidence. +- Contract mismatches: none + +## Waivers + +- Self-validation rationale: none + +## Files to Add/Modify + +- `feature_records/...` — fixture only + +## Testing Strategy + +Run the checker and expect failure. + +## Open Questions + +None. diff --git a/upcoming_features/.gitignore b/upcoming_features/.gitignore deleted file mode 100644 index 91dee74..0000000 --- a/upcoming_features/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -* -!.gitignore -!*.md diff --git a/upcoming_features/TEMPLATE.md b/upcoming_features/TEMPLATE.md deleted file mode 100644 index 515f1f0..0000000 --- a/upcoming_features/TEMPLATE.md +++ /dev/null @@ -1,23 +0,0 @@ -# Feature: - -## Motivation - -Why this feature is needed and what problem it solves. - -## Proposed Behavior - -What the feature does from the user's perspective. Include examples if helpful. - -## Files to Add/Modify - -- `src/...` — description of changes -- `tests/...` — test coverage plan -- `CMakeLists.txt` — build integration (if applicable) - -## Testing Strategy - -How to verify the feature works correctly. Prefer deterministic, headless tests. - -## Open Questions - -Unresolved decisions or trade-offs that need input before implementation.