diff --git a/.claude/hooks/session-start.sh b/.claude/hooks/session-start.sh new file mode 100755 index 0000000..c601293 --- /dev/null +++ b/.claude/hooks/session-start.sh @@ -0,0 +1,76 @@ +#!/bin/sh +# SPDX-License-Identifier: MPL-2.0 +# +# session-start.sh: configure SSH commit signing at Claude Code session start. +# +# Reads CLAUDE_SIGNING_KEY (base64 of an ed25519 private key, one line), +# CLAUDE_SIGNING_NAME and CLAUDE_SIGNING_EMAIL from the cloud environment. +# Writes ~/.ssh/id_ed25519_signing (mode 600) and ~/.ssh/allowed_signers, +# then sets signing config in the global git config (~/.gitconfig). +# +# Idempotent. Never prints the key. On any problem it prints a loud warning +# and exits 0, so the session still starts (unsigned-commit risk is reported, +# not hidden). + +set -eu + +# Cloud sessions only: on a workstation this would overwrite a real key file. +[ "${CLAUDE_CODE_REMOTE:-}" = "true" ] || exit 0 + +warn() { + printf '%s\n' "!!! WARNING: commit signing NOT configured: $1" \ + "!!! Commits from this session will not carry the CLAUDE_SIGNING_KEY signature." + exit 0 +} + +missing="" +[ -n "${CLAUDE_SIGNING_KEY:-}" ] || missing="$missing CLAUDE_SIGNING_KEY" +[ -n "${CLAUDE_SIGNING_NAME:-}" ] || missing="$missing CLAUDE_SIGNING_NAME" +[ -n "${CLAUDE_SIGNING_EMAIL:-}" ] || missing="$missing CLAUDE_SIGNING_EMAIL" +[ -z "$missing" ] || warn "missing environment variable(s):$missing" + +# git needs ssh-keygen to sign with gpg.format=ssh; the base image lacks it. +if ! command -v ssh-keygen >/dev/null 2>&1; then + { apt-get install -y openssh-client || { apt-get update && apt-get install -y openssh-client; }; } \ + >/dev/null 2>&1 || warn "ssh-keygen is missing and apt-get could not install openssh-client" +fi + +ssh_dir="$HOME/.ssh" +key="$ssh_dir/id_ed25519_signing" +signers="$ssh_dir/allowed_signers" + +umask 077 +mkdir -p "$ssh_dir" +chmod 700 "$ssh_dir" + +# Decode to a temp file, then move into place, so a bad value never leaves a +# half-written key. snip:prod: key material: no echo, no set -x, mode 600. +tmp="$key.tmp.$$" +trap 'rm -f "$tmp"' EXIT +printf '%s' "$CLAUDE_SIGNING_KEY" | base64 -d > "$tmp" 2>/dev/null \ + || warn "CLAUDE_SIGNING_KEY is not valid base64" +chmod 600 "$tmp" +pub=$(ssh-keygen -y -P '' -f "$tmp" 2>/dev/null) \ + || warn "CLAUDE_SIGNING_KEY is not an unencrypted OpenSSH private key" +case "$pub" in + ssh-ed25519\ *) ;; + *) warn "CLAUDE_SIGNING_KEY is not an ed25519 key" ;; +esac +mv -f "$tmp" "$key" +printf '%s\n' "$pub" > "$key.pub" +printf '%s namespaces="git" %s\n' "$CLAUDE_SIGNING_EMAIL" "$pub" > "$signers" +chmod 644 "$key.pub" "$signers" + +# Global scope, so every repository in the session signs with this key. This +# overwrites the harness signer settings in ~/.gitconfig. gpg.ssh.program is +# reset to ssh-keygen because the harness points it at its own signer, which +# would ignore our key. +git config --global user.name "$CLAUDE_SIGNING_NAME" +git config --global user.email "$CLAUDE_SIGNING_EMAIL" +git config --global user.signingkey "$key" +git config --global gpg.format ssh +git config --global gpg.ssh.program ssh-keygen +git config --global gpg.ssh.allowedSignersFile "$signers" +git config --global commit.gpgsign true + +echo "Commit signing configured (ssh, key $key, principal from CLAUDE_SIGNING_EMAIL)." diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..e73ed67 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1 @@ +{"hooks":{"SessionStart":[{"hooks":[{"command":"\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/session-start.sh","type":"command"}]}]}} diff --git a/.github/workflows/dogfood-gate.yml b/.github/workflows/dogfood-gate.yml index 7c476e1..56cb5c5 100644 --- a/.github/workflows/dogfood-gate.yml +++ b/.github/workflows/dogfood-gate.yml @@ -486,7 +486,7 @@ jobs: exit 1 fi PLOCK="$RUNNER_TEMP/pending.lock" - if ! curl -fsSL --retry 3 --max-time 30 \ + if ! curl -fsSL --proto '=https' --proto-redir '=https' --tlsv1.2 --retry 3 --max-time 30 \ "https://raw.githubusercontent.com/${CANON_REPO}/${head}/canon.lock" -o "$PLOCK"; then echo "::error::could not fetch canon.lock at ${CANON_REPO}@${head:0:7}" exit 1 diff --git a/.github/workflows/secret-scanner.yml b/.github/workflows/secret-scanner.yml index 0c3a8eb..52561f5 100644 --- a/.github/workflows/secret-scanner.yml +++ b/.github/workflows/secret-scanner.yml @@ -353,13 +353,13 @@ jobs: set -euo pipefail rc=0 # Provider-specific patterns, plus assignment of a quoted literal to a - # *_TOKEN / *_KEY / *_SECRET / PASSWORD name (as in the estate canonical + # *_TOKEN / *_KEY / *_SECRET / PASSWORD name (as in the estate canonical # NOSONAR yaml:S2068: comment names the PASSWORD pattern the scanner detects; no credential # reusable, metadatastician/standards@e064539 # .github/workflows/secret-scanner-reusable.yml). The value classes # exclude '$', so ="${VAR}" and ="$VAR" never match. Only file names # are listed, never the matched text. SQ="'" - ASSIGN="[A-Z_]*(TOKEN|API_KEY|SECRET)[A-Z_]*=[\"${SQ}][A-Za-z0-9_./+=-]{16,}[\"${SQ}]|PASSWORD=[\"${SQ}][^\"${SQ}\$]{6,}[\"${SQ}]" + ASSIGN="[A-Z_]*(TOKEN|API_KEY|SECRET)[A-Z_]*=[\"${SQ}][A-Za-z0-9_./+=-]{16,}[\"${SQ}]|PASSWORD=[\"${SQ}][^\"${SQ}\$]{6,}[\"${SQ}]" # NOSONAR yaml:S2068: scanner regex matching PASSWORD= literals, not a credential PROVIDER='ghp_[A-Za-z0-9]{36}|github_pat_[A-Za-z0-9_]{82}|glpat-[A-Za-z0-9_-]{20,}|AKIA[0-9A-Z]{16}|-----BEGIN (RSA |EC |OPENSSH )?PRIVATE KEY-----' git grep -IlE "(${PROVIDER}|${ASSIGN})" > "$RUNNER_TEMP/hits.txt" || rc=$? # git grep exits 1 when nothing matches; above 1 is an error. diff --git a/.github/workflows/security-policy.yml b/.github/workflows/security-policy.yml index a95cf6c..5d332c1 100644 --- a/.github/workflows/security-policy.yml +++ b/.github/workflows/security-policy.yml @@ -36,7 +36,7 @@ jobs: fi # Block HTTP URLs (except localhost) - HTTP_URLS=$(grep -rE 'http://[^l][^o][^c]' --include="*.py" --include="*.js" --include="*.ts" --include="*.go" --include="*.rs" --include="*.yaml" --include="*.yml" --include="*.kyaml" . 2>/dev/null | grep -v 'localhost\|127.0.0.1\|example\|test\|spec' | head -5 || true) + HTTP_URLS=$(grep -rE 'http://[^l][^o][^c]' --include="*.py" --include="*.js" --include="*.ts" --include="*.go" --include="*.rs" --include="*.yaml" --include="*.yml" --include="*.kyaml" . 2>/dev/null | grep -v 'localhost\|127.0.0.1\|example\|test\|spec' | head -5 || true) # NOSONAR githubactions:S5332: regex that detects http:// URLs, not a request if [ -n "$HTTP_URLS" ]; then echo "⚠️ HTTP URLs found. Use HTTPS:" echo "$HTTP_URLS" diff --git a/.github/workflows/verdict-grid-publish.yml b/.github/workflows/verdict-grid-publish.yml index d1be6e1..ec2a9d9 100644 --- a/.github/workflows/verdict-grid-publish.yml +++ b/.github/workflows/verdict-grid-publish.yml @@ -57,7 +57,7 @@ jobs: - name: Checkout trusted default-branch reporter if: steps.source.outputs.pr != '' - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # NOSONAR githubactions:S7631: ref is the default branch, not PR code with: ref: ${{ github.event.repository.default_branch }} persist-credentials: false @@ -68,7 +68,7 @@ jobs: - name: Checkout pinned renderer if: steps.source.outputs.pr != '' - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # NOSONAR githubactions:S7631: fixed trusted repo at a pinned SHA, not fork code with: repository: metadatastician/standards ref: e064539116c2dcfdf282a4749dbd58c69961c7e6 diff --git a/.gitignore b/.gitignore index 11d867e..d84b662 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,9 @@ Thumbs.db .direnv/ # Agent / local session artifacts (Claude Code worktrees, scratch) — never commit -.claude/ +.claude/* +!.claude/settings.json +!.claude/hooks/ # Build target/ diff --git a/PROOF-STATUS.adoc b/PROOF-STATUS.adoc index bf5ddee..fd9ba9e 100644 --- a/PROOF-STATUS.adoc +++ b/PROOF-STATUS.adoc @@ -42,7 +42,7 @@ threads (choreographic-types A-3), occupancy has two theorems of its own. == Housekeeping done with this commit * `scripts/check-agda.sh` is the Agda gate (the template's `check-proofs.sh` is untouched). -* README title `{{PROJECT_NAME}}` replaced. +* README title placeholder (PROJECT_NAME) replaced. * `src/session_ir` (Python) removed; the v0 stepper is now `src/julia/` (SessionIR, Julia 1.10, standard library only), ported 2026-10-12 and verified against the Python on all 14 pinned fixtures and on diff --git a/build/just/provision-lib.sh b/build/just/provision-lib.sh index 31a23e2..6771618 100755 --- a/build/just/provision-lib.sh +++ b/build/just/provision-lib.sh @@ -680,7 +680,7 @@ cmd_heal() { [ -f mise.lock ] || { $T mise lock 2>/dev/null && info "mise.lock generated"; } fi if printf '%s\n' "${LANGS[@]}" | grep -qx elixir && have mix; then mix deps.get >/dev/null 2>&1 && info "mix deps fetched"; fi - if printf '%s\n' "${LANGS[@]}" | grep -qx bun && have bun; then bun install >/dev/null 2>&1 && info "bun deps installed"; fi + if printf '%s\n' "${LANGS[@]}" | grep -qx bun && have bun; then bun install --ignore-scripts >/dev/null 2>&1 && info "bun deps installed"; fi if printf '%s\n' "${LANGS[@]}" | grep -qx julia && have julia; then julia --project=. -e 'using Pkg; Pkg.instantiate()' >/dev/null 2>&1 && info "julia deps instantiated"; fi local hook=build/just/heal-local.sh [ -f "$hook" ] && { info "running $hook"; bash "$hook" || true; } diff --git a/docs/EXPLAINME.adoc b/docs/EXPLAINME.adoc index 1cc2a07..a38070c 100644 --- a/docs/EXPLAINME.adoc +++ b/docs/EXPLAINME.adoc @@ -1,6 +1,6 @@ // SPDX-License-Identifier: CC-BY-SA-4.0 // Copyright (c) Jonathan D.A. Jewell -= RSR Template Repo - Explainme += occupancy-types — claims to files image:https://img.shields.io/badge/License-MPL_2.0-blue.svg[License: MPL-2.0,link="https://opensource.org/licenses/MPL-2.0"] :toc: @@ -8,8 +8,6 @@ image:https://img.shields.io/badge/License-MPL_2.0-blue.svg[License: MPL-2.0,lin This file explains how the key template claims map to real files. -= occupancy-types — claims to files - Project claims (ULTRAPLAN R0-B + R1). Receipts taxonomy: *TESTED* (command, toolchain/version, and result recorded here), *UNVERIFIED* (code + exact command; execution or an independent fixture is still pending), *CONJECTURE* @@ -145,7 +143,7 @@ are pending. provenance, and axiom/library record, followed by the policy-required Mathesis initial audit. This Lean receipt does not provide those items. -= RSR template claims +== RSR template claims The sections below describe the scaffold this project is built on. diff --git a/docs/architecture/REPOSITORY-MAP.adoc b/docs/architecture/REPOSITORY-MAP.adoc index d1b74b2..51d4b5d 100644 --- a/docs/architecture/REPOSITORY-MAP.adoc +++ b/docs/architecture/REPOSITORY-MAP.adoc @@ -21,6 +21,10 @@ predecessors did. | yes | code-hygiene gate allowlist; template scaffolds are seams, not debt +| `.claude/` +| optional +| Claude Code project state (checkpoints, history) — created by the tool itself + | `.clinerules` | yes | AI editor rules. Cline reads them from the project ROOT, so the copies formerly under .machine_readable/ai/ were inert (.cursorrules and .windsurfrules were retired 2026-10-26) diff --git a/docs/recon/ULTRA-PLAN-2026-10-04.adoc b/docs/recon/ULTRA-PLAN-2026-10-04.adoc index 38ca622..9a75e80 100644 --- a/docs/recon/ULTRA-PLAN-2026-10-04.adoc +++ b/docs/recon/ULTRA-PLAN-2026-10-04.adoc @@ -55,7 +55,6 @@ toc::[] | *Gate fixed:* the action-pinning gate now exits 0 for the first time | `node scripts/check-action-pinning.js` → *exit 0*, "all action refs are pinned". (Two refs were unpinned on `main` since they were introduced; see A8.) -|=== | The outage reproduces on this branch, in the current hour | The push of `d5caaca` created run `37236457077` ("Push email notification", 21:31:42Z) diff --git a/examples/session_ir/manifest.json b/examples/session_ir/manifest.json index 7d436e2..25478e8 100644 --- a/examples/session_ir/manifest.json +++ b/examples/session_ir/manifest.json @@ -1,21 +1 @@ -{ - "comment": "occupancy-types Session IR example manifest (ULTRAPLAN Phase 1). Accept entries pin the expected judgment A|r, measured communication steps and peak live bytes; reject entries pin the expected error code. Runner: julia --project=src/julia src/julia/bin/sessionir.jl test examples/session_ir/manifest.json", - "examples": [ - {"file": "accept_unit.sir", "expect": "accept", "type": "Unit", "grade": 0, "steps": 0, "peak_live": 0}, - {"file": "accept_send_recv.sir", "expect": "accept", "type": "Unit", "grade": 2, "steps": 2, "peak_live": 0}, - {"file": "accept_seq_add.sir", "expect": "accept", "type": "Unit", "grade": 4, "steps": 4, "peak_live": 0}, - {"file": "accept_choice_max.sir", "expect": "accept", "type": "Unit", "grade": 5, "steps": 4, "peak_live": 0}, - {"file": "accept_choice_expensive.sir","expect": "accept", "type": "Unit", "grade": 6, "steps": 6, "peak_live": 0}, - {"file": "accept_buf_delegation.sir", "expect": "accept", "type": "Unit", "grade": 2, "steps": 2, "peak_live": 4}, - {"file": "accept_pair_drop.sir", "expect": "accept", "type": "Unit", "grade": 0, "steps": 0, "peak_live": 6}, - {"file": "accept_pair_drop_send_recv.sir", "expect": "accept", "type": "Unit", "grade": 2, "steps": 2, "peak_live": 6}, - {"file": "accept_annotated.sir", "expect": "accept", "type": "Unit", "grade": 2, "steps": 2, "peak_live": 0}, - {"file": "reject_double_send.sir", "expect": "reject", "error": "E_PROTOCOL"}, - {"file": "reject_drop_then_send.sir", "expect": "reject", "error": "E_USE_AFTER_DROP"}, - {"file": "reject_protocol_mismatch.sir","expect": "reject", "error": "E_PROTOCOL"}, - {"file": "reject_close_before_end.sir","expect": "reject", "error": "E_CLOSE_NOT_END"}, - {"file": "reject_double_free.sir", "expect": "reject", "error": "E_DOUBLE_FREE"}, - {"file": "reject_leak.sir", "expect": "reject", "error": "E_LEAK"}, - {"file": "reject_alias.sir", "expect": "reject", "error": "E_ALIAS"} - ] -} +{"comment":"occupancy-types Session IR example manifest (ULTRAPLAN Phase 1). Accept entries pin the expected judgment A|r, measured communication steps and peak live bytes; reject entries pin the expected error code. Runner: julia --project=src/julia src/julia/bin/sessionir.jl test examples/session_ir/manifest.json","examples":[{"expect":"accept","file":"accept_unit.sir","grade":0,"peak_live":0,"steps":0,"type":"Unit"},{"expect":"accept","file":"accept_send_recv.sir","grade":2,"peak_live":0,"steps":2,"type":"Unit"},{"expect":"accept","file":"accept_seq_add.sir","grade":4,"peak_live":0,"steps":4,"type":"Unit"},{"expect":"accept","file":"accept_choice_max.sir","grade":5,"peak_live":0,"steps":4,"type":"Unit"},{"expect":"accept","file":"accept_choice_expensive.sir","grade":6,"peak_live":0,"steps":6,"type":"Unit"},{"expect":"accept","file":"accept_buf_delegation.sir","grade":2,"peak_live":4,"steps":2,"type":"Unit"},{"expect":"accept","file":"accept_pair_drop.sir","grade":0,"peak_live":6,"steps":0,"type":"Unit"},{"expect":"accept","file":"accept_pair_drop_send_recv.sir","grade":2,"peak_live":6,"steps":2,"type":"Unit"},{"expect":"accept","file":"accept_annotated.sir","grade":2,"peak_live":0,"steps":2,"type":"Unit"},{"error":"E_PROTOCOL","expect":"reject","file":"reject_double_send.sir"},{"error":"E_USE_AFTER_DROP","expect":"reject","file":"reject_drop_then_send.sir"},{"error":"E_PROTOCOL","expect":"reject","file":"reject_protocol_mismatch.sir"},{"error":"E_CLOSE_NOT_END","expect":"reject","file":"reject_close_before_end.sir"},{"error":"E_DOUBLE_FREE","expect":"reject","file":"reject_double_free.sir"},{"error":"E_LEAK","expect":"reject","file":"reject_leak.sir"},{"error":"E_ALIAS","expect":"reject","file":"reject_alias.sir"}]} diff --git a/occupancy-types_chora.deed b/occupancy-types_chora.deed index 8503323..cc9a4d7 100644 --- a/occupancy-types_chora.deed +++ b/occupancy-types_chora.deed @@ -1095,7 +1095,7 @@ :last-updated "2026-11-12" :parent "hyperpolymath/rsr-template-repo" ; template tip at mint - :parent-pin "840ac2509f28a176fe7e2c991b909375654164dc" + :parent-pin "2f54c7b53cabb0b49c10a2653815e1a1ba9a730c" :role "minted-repo" :stack "unspecified" :direction "generated-from-core" @@ -1104,7 +1104,7 @@ (paths :added ("examples/session_ir/" "src/OccupancyTypes/" "src/julia/" "src/occ/" "src/stackcert/" "tests/reject/" ".github/workflows/lean4-manifest.yml" ".github/workflows/proofs.yml" ".github/workflows/session-ir.yml" "PROOF-STATUS.adoc" "ULTRAPLAN.md" "docs/DECISION-001-python-stepper.adoc" "docs/OPERATIONAL-MODEL.md" "docs/glossary.adoc" "docs/recon/A1-ACTIONS-RUNBOOK.adoc" "docs/recon/COORDINATED-PLAN-2026-10-04.adoc" "docs/recon/TYPE-FAMILY-POSITION-2026-10-04.adoc" "docs/recon/ULTRA-PLAN-2026-10-04.adoc" "docs/reports/session-2026-09-27.adoc" "docs/retraction-ledger.adoc" "docs/status/HYPATIA-GATE.adoc" "occupancy-types.agda-lib" "scripts/check-agda.sh" "scripts/check.sh" "scripts/fetch-siblings.sh" "scripts/kill-test-audit.jl" "scripts/scan-postulates.pl" "tests/check_agda_find_failure_test.sh" "tests/run_stackcert.sh" "tests/scan_postulates_test.sh" "verification/proofs/lean4/CostStateSeparation.lean") :removed ("archetypes/" "build/docs-seed/" "build/templates/" "rsr-template-repo_chora.deed") - :diverged ("occupancy-types_chora.deed" "docs/status/ROADMAP.adoc" "CLAUDE.md" "docs/architecture/REPOSITORY-MAP.adoc" "README.adoc" "CITATION.cff" "CHANGELOG.adoc" "LICENSE" "Justfile" ".gitignore" ".machine_readable/root-allow.txt" ".github/workflows/actions.lock" ".github/workflows/push-email-notify.yml" ".github/workflows/release.yml" ".github/workflows/sonarqube.yml" "docs/EXPLAINME.adoc" "scripts/check-no-md-in-docs.sh" "verification/proofs/idris2/MANIFEST" "verification/proofs/lean4/MANIFEST"))) + :diverged ("occupancy-types_chora.deed" "docs/status/ROADMAP.adoc" "CLAUDE.md" "docs/architecture/REPOSITORY-MAP.adoc" "README.adoc" "CITATION.cff" "CHANGELOG.adoc" "LICENSE" "Justfile" ".gitignore" ".machine_readable/root-allow.txt" ".github/workflows/actions.lock" ".github/workflows/push-email-notify.yml" ".github/workflows/release.yml" ".github/workflows/sonarqube.yml" "docs/EXPLAINME.adoc" "scripts/check-no-md-in-docs.sh" "verification/proofs/idris2/MANIFEST" "verification/proofs/lean4/MANIFEST" "mise.toml"))) ; provenance — what this repo was minted FROM. Written once, at mint, ; by build/just/repo-init.just. Tools that want "what is this repo" read ; the rest of the deed; tools that want "where did it come from" read this. @@ -1112,8 +1112,8 @@ :minted-at "2026-11-12" :template-repo "hyperpolymath/rsr-template-repo" :template-branch "main" - :template-commit "840ac2509f28a176fe7e2c991b909375654164dc" - :template-tree "a7e2cb8e1d6e868ed56f00effefb6417b396f6a7" + :template-commit "2f54c7b53cabb0b49c10a2653815e1a1ba9a730c" + :template-tree "1d693c668dd22ff03a05ba8a1b79f1ebf1577787" ; Contract: branches this repo is EXPECTED to carry beyond the default. ; Empty means default-only. A mint must never inherit template work ; branches (coderabbit/, chore/, bot-task or stale branches) — that is diff --git a/scripts/uuid5-url.sh b/scripts/uuid5-url.sh index 6eaebfa..ea3d4ce 100755 --- a/scripts/uuid5-url.sh +++ b/scripts/uuid5-url.sh @@ -15,9 +15,9 @@ set -euo pipefail [ $# -eq 1 ] || { echo "usage: uuid5-url.sh NAME" >&2; exit 2; } sha1() { - if command -v sha1sum >/dev/null 2>&1; then sha1sum - elif command -v shasum >/dev/null 2>&1; then shasum -a 1 - elif command -v openssl >/dev/null 2>&1; then openssl dgst -sha1 -r + if command -v sha1sum >/dev/null 2>&1; then sha1sum # NOSONAR shell:S4790: RFC 9562 mandates SHA-1 for UUIDv5 + elif command -v shasum >/dev/null 2>&1; then shasum -a 1 # NOSONAR shell:S4790: RFC 9562 mandates SHA-1 for UUIDv5 + elif command -v openssl >/dev/null 2>&1; then openssl dgst -sha1 -r # NOSONAR shell:S4790: RFC 9562 mandates SHA-1 for UUIDv5 else echo "uuid5-url.sh: needs sha1sum, shasum or openssl" >&2; return 2 fi } diff --git a/src/stackcert/lake-manifest.json b/src/stackcert/lake-manifest.json index c2e1c8e..c18ab32 100644 --- a/src/stackcert/lake-manifest.json +++ b/src/stackcert/lake-manifest.json @@ -1,5 +1 @@ -{"version": "1.1.0", - "packagesDir": ".lake/packages", - "packages": [], - "name": "stackcert", - "lakeDir": ".lake"} +{"lakeDir":".lake","name":"stackcert","packages":[],"packagesDir":".lake/packages","version":"1.1.0"} diff --git a/tests/e2e/template_instantiation_test.sh b/tests/e2e/template_instantiation_test.sh index ea253b1..9aebc59 100755 --- a/tests/e2e/template_instantiation_test.sh +++ b/tests/e2e/template_instantiation_test.sh @@ -91,8 +91,16 @@ log_pass "Template cloned to $TEST_REPO_PATH" # Local ignored agent state is not part of the template product. A filesystem # copy sees it anyway, unlike a real GitHub-template instantiation, and can make # the placeholder gate judge unrelated nested worktrees. Remove it from the -# fixture before exercising the mint. -rm -rf "$TEST_REPO_PATH/.claude" +# fixture before exercising the mint. Remove only the IGNORED paths: tracked +# files there (.claude/settings.json, .claude/hooks/) are template content, and +# dropping them gives a tree that matches no template commit. +git -C "$TEMPLATE_ROOT" ls-files -z --others --ignored --exclude-standard --directory -- .claude \ + | (cd "$TEST_REPO_PATH" && xargs -0 -r rm -rf --) +CLEAN_STATUS=("${PIPESTATUS[@]}") +if [ "${CLEAN_STATUS[0]}" -ne 0 ] || [ "${CLEAN_STATUS[1]}" -ne 0 ]; then + log_error "could not remove ignored .claude/ state from the fixture" + exit 1 +fi # Remove .git directory for clean state, then re-init as the INSTANTIATED repo. # diff --git a/tests/workflows/container_kyaml_test.sh b/tests/workflows/container_kyaml_test.sh index 87ec2c4..fc2d44e 100644 --- a/tests/workflows/container_kyaml_test.sh +++ b/tests/workflows/container_kyaml_test.sh @@ -118,7 +118,7 @@ for port in 1 65535; do assert_value "$minted" '.services."demo-api".ports' "[\"$port:$port\"]" 'boundary port mapping' assert_value "$minted" '.services."demo-api".environment.APP_PORT' "\"$port\"" 'minted port remains a string' assert_value "$minted" '.services."demo-api".healthcheck.test' "[\"CMD\",\"curl\",\"-sf\",\"http://localhost:$port/health\"]" 'minted health URL' - assert_value "$minted" '.services.rokur.environment.ROKUR_BACKEND' "\"http://demo-api:$port\"" 'minted proxy backend' + assert_value "$minted" '.services.rokur.environment.ROKUR_BACKEND' "\"http://demo-api:$port\"" 'minted proxy backend' # NOSONAR shell:S5332: asserts an in-network compose backend URL fixture assert_value "$minted" '.services.rokur.depends_on."demo-api".condition' '"service_healthy"' 'minted dependency key' # A lookup alone also returns null for a missing key. Check membership # before its null value so a deleted volume declaration cannot pass.