diff --git a/CHANGELOG.md b/CHANGELOG.md index 93910bc..e0b2d28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,12 @@ dated release section after the final merged commit is selected. --> ### Fixed +- Preserved literal PATH entries during `base_std_*` PATH deduplication. +- Rejected non-root Git update paths so `base_git_update_repo` cannot update an + ancestor repository from a plain child directory. +- Preserved exact string and list semantics under Bash `nocasematch`. +- Validated integer ranges before arithmetic so out-of-range configuration + values are rejected instead of being narrowed by conversion. - Fixed nested configuration and validator state leaking across application loads and nested CLI traversals. - Fixed generated completion handling at the cursor/end-of-array boundary and diff --git a/docs/consumer-validation-status.md b/docs/consumer-validation-status.md index be9025f..deb0e48 100644 --- a/docs/consumer-validation-status.md +++ b/docs/consumer-validation-status.md @@ -1,6 +1,10 @@ # Consumer validation status -Last validated: 2026-09-02 +Ledger reconciled: 2026-10-07 + +The v2.0.0 rows below are historical GA evidence. The current canary snapshot +is recorded separately and must not be read as a new first-party release or +consumer pin. This is an internal readiness ledger for the #239 adoption work. It records what can be reproduced from the current local checkouts; it is not a public @@ -10,13 +14,22 @@ has approved publication. ## Framework reference -The release gate used the immutable `base-bash-libs` v2.0.0 GA at merge commit +Historical GA release gate: immutable `base-bash-libs` v2.0.0 at merge commit `b4243765726c133499feeabdc50154f99c0fec12`. The [v2.0.0 release](https://github.com/basefoundry/base-bash-libs/releases/tag/v2.0.0) archive, SHA256SUMS, SPDX SBOM, and provenance assets were downloaded and verified. The canonical archive SHA256 is `73d6f92fab8f1a8ded7f3b4312ebbe51aa8ec0c16eacf18c2d8fa23fb5664333`. +## Current canary snapshot + +On 2026-10-05, the scheduled [Framework Compatibility run +#37327900680](https://github.com/basefoundry/base-bash-libs-demo/actions/runs/37327900680) +passed all four candidate jobs: the immutable v2.0.0 baseline and current +supported v2.1.0 release on Ubuntu 24.04 and macOS 14. This validates the demo's +black-box compatibility path; it does not change the committed v2.0.0 vendor +pin or promote the historical GA rows below. + ## Consumer matrix | Checkout | Relationship | Reference | Deployment exercised | Result | Classification | diff --git a/docs/discovery/awesome-bash.md b/docs/discovery/awesome-bash.md index f01b1da..5115b67 100644 --- a/docs/discovery/awesome-bash.md +++ b/docs/discovery/awesome-bash.md @@ -5,8 +5,9 @@ deliberately not a claim that the project has been accepted upstream. ## Submission gate -Submit only after the v2.0.0 release has a canonical immutable asset and the -following links are public and stable: +Submit only after a published v2 release has a canonical immutable asset and +the following links are public and stable. This record was last reviewed on +2026-10-07 against v2.1.0: 1. the five-minute adoption path in [`docs/v2/quickstart.md`](../v2/quickstart.md); 2. the support and security policies in [`docs/support-policy.md`](../support-policy.md); @@ -25,8 +26,8 @@ following links are public and stable: ## Maintainer checklist -- [ ] v2.0.0 release URL and SHA-256 added to this record -- [ ] release asset, bundle, and provenance links resolve without credentials -- [ ] reference-app and benchmark links resolve on the default branch +- [ ] v2.0.0 release URL and SHA-256 are recorded in this submission record +- [ ] release asset, bundle, and provenance links are independently verified here +- [ ] reference-app and benchmark links are independently verified here - [ ] submission PR URL recorded here - [ ] upstream acceptance or rejection recorded here diff --git a/docs/first-party-cutover.md b/docs/first-party-cutover.md index c97e3de..0081a06 100644 --- a/docs/first-party-cutover.md +++ b/docs/first-party-cutover.md @@ -1,4 +1,8 @@ -# First-party v2 cutover +# Historical first-party v2.0.0 cutover record + +Record scope: historical GA handoff completed on 2026-08-15. This page and its +machine-readable inventory describe the v2.0.0 cutover; they are not the +current Base or Base Bash release status. Issue #240 records the first-party promotion handoff. The exact v2 GA asset, checksum, and provenance exist, and Base, Base Demo, and Homebrew now point at @@ -37,8 +41,10 @@ the v2 asset/tag unchanged. Never retag a different commit or silently fall back to a moving branch. Record the failure and corrected checksums in the release notes, then resume from the failed consumer after a reviewed fix. -## Current state +## Historical state at v2.0.0 GA Base's `basectl help` integration defect is fixed in the merged issue-backed PR -above and validated locally against the v2 API. Base v1.8.0, Base Demo, and the -Homebrew `base-v1.8.0` bottle release consume the verified v2.0.0 GA asset. +above and validated locally against the v2 API. At the 2026-08-15 GA cutover, +Base v1.8.0, Base Demo, and the Homebrew `base-v1.8.0` bottle release consumed +the verified v2.0.0 GA asset. Later releases and pins belong in their own +release records. diff --git a/docs/integrations.md b/docs/integrations.md index 539e294..0f8e8bb 100644 --- a/docs/integrations.md +++ b/docs/integrations.md @@ -52,10 +52,11 @@ in CI rather than making them runtime dependencies. ## Package channels `integrations/package-managers/registry.yaml` is an intentionally conservative -registry. bpkg, Basher, and Basalt entries remain `planned-after-v2-ga` until an -immutable v2 asset, checksum, provenance, and a maintained update path exist. -No package-manager URL in this repository is presented as an official install -source before those gates pass. +registry. bpkg, Basher, and Basalt entries remain +`deferred-until-maintained-update-path`: the immutable v2 asset, checksum, and +provenance gates have passed, but no maintained owner and update path has been +verified. No package-manager URL in this repository is presented as an official +install source before that remaining gate passes. The exact upstream revisions reviewed for the adapters and project tools are recorded in [`integrations/compatibility.yaml`](../integrations/compatibility.yaml). diff --git a/examples/reference-apps/README.md b/examples/reference-apps/README.md index 83f12f8..b412152 100644 --- a/examples/reference-apps/README.md +++ b/examples/reference-apps/README.md @@ -34,6 +34,7 @@ examples/reference-apps/release-rehearsal.sh \ ``` The required evidence schema and platform matrix live in -[`release-evidence.yaml`](release-evidence.yaml). It records the verified -canonical `v2.0.0` asset, checksum, and provenance; the repository never treats -a moving checkout as release evidence. +[`release-evidence.yaml`](release-evidence.yaml). That file is a historical +v2.0.0 GA baseline, reviewed on 2026-10-07; it records the verified canonical +asset, checksum, and provenance, and the repository never treats a moving +checkout as release evidence. diff --git a/examples/reference-apps/release-evidence.yaml b/examples/reference-apps/release-evidence.yaml index 42dc705..875cdf2 100644 --- a/examples/reference-apps/release-evidence.yaml +++ b/examples/reference-apps/release-evidence.yaml @@ -1,4 +1,6 @@ schema_version: 1 +record_scope: historical-v2.0.0-ga-baseline +last_reviewed: 2026-10-07 release_line: v2.0.0 status: verified diff --git a/first-party-cutover.yaml b/first-party-cutover.yaml index c8de679..145d4a9 100644 --- a/first-party-cutover.yaml +++ b/first-party-cutover.yaml @@ -1,4 +1,7 @@ schema_version: 1 +record_scope: historical-v2.0.0-ga-cutover +record_date: 2026-08-15 +last_reconciled: 2026-10-07 target_release: 2.0.0 release_status: published required_asset: diff --git a/integrations/compatibility.yaml b/integrations/compatibility.yaml index 2bd85e3..01f89d3 100644 --- a/integrations/compatibility.yaml +++ b/integrations/compatibility.yaml @@ -1,4 +1,6 @@ schema_version: 1 +record_scope: historical-v2.0.0-ga-baseline +last_reviewed: 2026-10-07 framework_release_line: v2.0.0 update_policy: review-pins-on-every-upstream-release diff --git a/integrations/package-managers/registry.yaml b/integrations/package-managers/registry.yaml index 5300930..96c32de 100644 --- a/integrations/package-managers/registry.yaml +++ b/integrations/package-managers/registry.yaml @@ -3,17 +3,17 @@ artifact: base-bash-libs release_policy: immutable-v2-asset-and-sha256 entries: - name: bpkg - status: planned-after-v2-ga + status: deferred-until-maintained-update-path install_metadata: publish-only-after-verified-release-asset verification: release-asset-sha256-and-provenance maintenance: explicit-owner-and-update-path-required - name: basher - status: planned-after-v2-ga + status: deferred-until-maintained-update-path install_metadata: publish-only-after-verified-release-asset verification: release-asset-sha256-and-provenance maintenance: explicit-owner-and-update-path-required - name: basalt - status: planned-after-v2-ga + status: deferred-until-maintained-update-path install_metadata: publish-only-after-verified-release-asset verification: release-asset-sha256-and-provenance maintenance: explicit-owner-and-update-path-required diff --git a/scripts/first-party-cutover b/scripts/first-party-cutover index 255b249..31cac1f 100755 --- a/scripts/first-party-cutover +++ b/scripts/first-party-cutover @@ -15,20 +15,69 @@ usage() { while (($#)); do case "$1" in - check) ;; - --allow-pending) allow_pending=1 ;; - --base-dir) [[ -n "${2-}" ]] || { usage >&2; exit 2; }; base_dir="$2"; shift ;; - --base-demo-dir) [[ -n "${2-}" ]] || { usage >&2; exit 2; }; base_demo_dir="$2"; shift ;; - --homebrew-dir) [[ -n "${2-}" ]] || { usage >&2; exit 2; }; homebrew_dir="$2"; shift ;; - -h|--help) usage; exit 0 ;; - *) printf 'Unknown cutover option: %s\n' "$1" >&2; usage >&2; exit 2 ;; + check) ;; + --allow-pending) allow_pending=1 ;; + --base-dir) + [[ -n "${2-}" ]] || { + usage >&2 + exit 2 + } + base_dir="$2" + shift + ;; + --base-demo-dir) + [[ -n "${2-}" ]] || { + usage >&2 + exit 2 + } + base_demo_dir="$2" + shift + ;; + --homebrew-dir) + [[ -n "${2-}" ]] || { + usage >&2 + exit 2 + } + homebrew_dir="$2" + shift + ;; + -h | --help) + usage + exit 0 + ;; + *) + printf 'Unknown cutover option: %s\n' "$1" >&2 + usage >&2 + exit 2 + ;; esac shift done -[[ -f "$manifest" ]] || { printf 'Missing cutover manifest: %s\n' "$manifest" >&2; exit 1; } -grep -F 'target_release: 2.0.0' "$manifest" >/dev/null || { printf 'Cutover target must be 2.0.0.\n' >&2; exit 1; } -grep -F 'tag: v2.0.0' "$manifest" >/dev/null || { printf 'Cutover tag must be v2.0.0.\n' >&2; exit 1; } +[[ -f "$manifest" ]] || { + printf 'Missing cutover manifest: %s\n' "$manifest" >&2 + exit 1 +} +grep -F 'record_scope: historical-v2.0.0-ga-cutover' "$manifest" > /dev/null || { + printf 'Cutover manifest must identify its historical record scope.\n' >&2 + exit 1 +} +grep -F 'record_date: 2026-08-15' "$manifest" > /dev/null || { + printf 'Cutover manifest must identify the v2.0.0 GA record date.\n' >&2 + exit 1 +} +grep -Eq '^last_reconciled: [0-9]{4}-[0-9]{2}-[0-9]{2}$' "$manifest" || { + printf 'Cutover manifest reconciliation date is missing or malformed.\n' >&2 + exit 1 +} +grep -F 'target_release: 2.0.0' "$manifest" > /dev/null || { + printf 'Cutover target must be 2.0.0.\n' >&2 + exit 1 +} +grep -F 'tag: v2.0.0' "$manifest" > /dev/null || { + printf 'Cutover tag must be v2.0.0.\n' >&2 + exit 1 +} release_status="$(sed -n 's/^release_status:[[:space:]]*//p' "$manifest" | sed -n '1p')" if [[ "$release_status" != published ]]; then @@ -44,26 +93,37 @@ check_no_legacy() { local directory="$1" [[ -n "$directory" && -d "$directory" ]] || return 0 if grep -R -n -E 'base_bash_libs_(require_version|std_|file_|git_|gh_|str_|arg_|list_)' \ - "$directory" --include='*.sh' --include='*.bash' --include='*.bats' 2>/dev/null; then + "$directory" --include='*.sh' --include='*.bash' --include='*.bats' 2> /dev/null; then printf 'Legacy base-bash-libs symbols remain under %s.\n' "$directory" >&2 return 1 fi } if [[ -n "$base_dir" ]]; then - [[ -f "$base_dir/base_init.sh" ]] || { printf 'Base directory lacks base_init.sh: %s\n' "$base_dir" >&2; exit 1; } - grep -F 'base_require_version' "$base_dir/base_init.sh" >/dev/null || { - printf 'Base does not call the v2 version helper.\n' >&2; exit 1; + [[ -f "$base_dir/base_init.sh" ]] || { + printf 'Base directory lacks base_init.sh: %s\n' "$base_dir" >&2 + exit 1 + } + grep -F 'base_require_version' "$base_dir/base_init.sh" > /dev/null || { + printf 'Base does not call the v2 version helper.\n' >&2 + exit 1 } check_no_legacy "$base_dir" || exit $? fi check_no_legacy "$base_demo_dir" || exit $? if [[ -n "$homebrew_dir" ]]; then formula="$homebrew_dir/Formula/base-bash-libs.rb" - [[ -f "$formula" ]] || { printf 'Homebrew formula is missing: %s\n' "$formula" >&2; exit 1; } - grep -F 'v2.0.0' "$formula" >/dev/null || { printf 'Homebrew formula is not pinned to v2.0.0.\n' >&2; exit 1; } - grep -E 'type -t (std_run|update_file_section|git_get_current_branch|gh_run)' "$formula" >/dev/null && { - printf 'Homebrew formula still tests legacy generic symbols.\n' >&2; exit 1; + [[ -f "$formula" ]] || { + printf 'Homebrew formula is missing: %s\n' "$formula" >&2 + exit 1 + } + grep -F 'v2.0.0' "$formula" > /dev/null || { + printf 'Homebrew formula is not pinned to v2.0.0.\n' >&2 + exit 1 + } + grep -E 'type -t (std_run|update_file_section|git_get_current_branch|gh_run)' "$formula" > /dev/null && { + printf 'Homebrew formula still tests legacy generic symbols.\n' >&2 + exit 1 } fi diff --git a/tests/docs-contract.sh b/tests/docs-contract.sh index aa3b7b6..dec83ac 100755 --- a/tests/docs-contract.sh +++ b/tests/docs-contract.sh @@ -44,11 +44,15 @@ readme_pin="$(sed -n '/^Pin the checkout to the full current release commit/,/^` printf 'README current-release pin does not match the expected v2.1.0 commit.\n' >&2 exit 1 } -resolved_current_release="$(git rev-parse --verify "${readme_current_release}^{commit}" 2> /dev/null || true)" -[[ "$resolved_current_release" == "$readme_pin" ]] || { - printf 'README current-release pin does not match its tag.\n' >&2 - exit 1 -} +if resolved_current_release="$(git rev-parse --verify "${readme_current_release}^{commit}" 2> /dev/null)"; then + [[ "$resolved_current_release" == "$readme_pin" ]] || { + printf 'README current-release pin does not match its tag.\n' >&2 + exit 1 + } +else + printf 'Skipping README current-release tag comparison: tag %s is unavailable in this checkout.\n' \ + "$readme_current_release" >&2 +fi grep -F 'framework_launcher' docs/v2/quickstart.md > /dev/null || { printf "The v2 quickstart must use the verified launcher path.\n" >&2 exit 1 diff --git a/tests/integration-release-contract.sh b/tests/integration-release-contract.sh index 26671ef..8074ef0 100755 --- a/tests/integration-release-contract.sh +++ b/tests/integration-release-contract.sh @@ -15,6 +15,10 @@ integration_fail() { [[ -f "$integration_manifest" ]] || integration_fail "missing compatibility manifest" grep -Fx 'schema_version: 1' "$integration_manifest" > /dev/null || integration_fail 'schema version is not 1' +grep -Fx 'record_scope: historical-v2.0.0-ga-baseline' "$integration_manifest" > /dev/null || + integration_fail 'compatibility manifest must identify its historical baseline scope' +grep -Eq '^last_reviewed: [0-9]{4}-[0-9]{2}-[0-9]{2}$' "$integration_manifest" || + integration_fail 'compatibility manifest review date is missing or malformed' grep -Fx 'framework_release_line: v2.0.0' "$integration_manifest" > /dev/null || integration_fail 'framework release line is not v2.0.0' @@ -37,8 +41,8 @@ done for channel in bpkg basher basalt; do grep -A2 -E "^ - name: ${channel}$" "$integration_repo_root/integrations/package-managers/registry.yaml" | - grep -F 'status: planned-after-v2-ga' > /dev/null || - integration_fail "package channel ${channel} must remain fail-closed before GA" + grep -F 'status: deferred-until-maintained-update-path' > /dev/null || + integration_fail "package channel ${channel} must remain deferred until a maintained update path exists" done printf 'Integration release contract passed: pins=%s package_channels=3.\n' "$pin_count" diff --git a/tests/integrations.bats b/tests/integrations.bats index 6d60be3..1310ba2 100644 --- a/tests/integrations.bats +++ b/tests/integrations.bats @@ -11,11 +11,11 @@ setup() { [[ "$output" == *"release=candidate"* ]] } -@test "integration recipes remain optional and package registry stays pre-GA" { - run grep -F 'status: planned-after-v2-ga' "$repo_root/integrations/package-managers/registry.yaml" +@test "integration recipes remain optional and package registry records its remaining gate" { + run grep -F 'status: deferred-until-maintained-update-path' "$repo_root/integrations/package-managers/registry.yaml" [ "$status" -eq 0 ] - [ "$(grep -c 'status: planned-after-v2-ga' "$repo_root/integrations/package-managers/registry.yaml")" -eq 3 ] + [ "$(grep -c 'status: deferred-until-maintained-update-path' "$repo_root/integrations/package-managers/registry.yaml")" -eq 3 ] } @test "project kit declares Bash-aware static-analysis policy" { diff --git a/tests/reference-release-contract.sh b/tests/reference-release-contract.sh index bc97baa..ba80231 100755 --- a/tests/reference-release-contract.sh +++ b/tests/reference-release-contract.sh @@ -14,6 +14,10 @@ reference_release_fail() { [[ -f "$reference_release_evidence" ]] || reference_release_fail 'evidence manifest is missing' grep -Fx 'schema_version: 1' "$reference_release_evidence" > /dev/null || reference_release_fail 'schema version is not 1' +grep -Fx 'record_scope: historical-v2.0.0-ga-baseline' "$reference_release_evidence" > /dev/null || + reference_release_fail 'release evidence must identify its historical baseline scope' +grep -Eq '^last_reviewed: [0-9]{4}-[0-9]{2}-[0-9]{2}$' "$reference_release_evidence" || + reference_release_fail 'release evidence review date is missing or malformed' grep -Fx 'release_line: v2.0.0' "$reference_release_evidence" > /dev/null || reference_release_fail 'release line is not v2.0.0' grep -Fx 'rehearsal_command: examples/reference-apps/release-rehearsal.sh' \