diff --git a/.github/workflows/new-project-governance.yml b/.github/workflows/new-project-governance.yml index 3ccf636..835fbd2 100644 --- a/.github/workflows/new-project-governance.yml +++ b/.github/workflows/new-project-governance.yml @@ -19,7 +19,7 @@ jobs: - name: Check out governed repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Acquire GitHub branch lifecycle snapshot - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: BRANCH_LIFECYCLE_SNAPSHOT: ${{ runner.temp }}/new-project-branch-lifecycle.json with: diff --git a/.governance/manifest.base.json b/.governance/manifest.base.json index 596f1ed..e36299e 100644 --- a/.governance/manifest.base.json +++ b/.governance/manifest.base.json @@ -110,7 +110,7 @@ "stacks": [], "standard": { "id": "wellmanifest/new-project", - "version": "0.20.12" + "version": "0.20.13" }, "ticket": { "activeStatuses": [ diff --git a/.governance/manifest.json b/.governance/manifest.json index 8ac4b17..014aed4 100644 --- a/.governance/manifest.json +++ b/.governance/manifest.json @@ -194,7 +194,7 @@ ], "standard": { "id": "wellmanifest/new-project", - "version": "0.20.12" + "version": "0.20.13" }, "ticket": { "activeStatuses": [ diff --git a/.governance/manifest.lock.json b/.governance/manifest.lock.json index 8b3de96..b10cd2f 100644 --- a/.governance/manifest.lock.json +++ b/.governance/manifest.lock.json @@ -4,7 +4,7 @@ ".cursor/rules/new-project-standard.mdc": "72699a5cb3718be9a50603eda9c8a6d960f21b1949fdfe2b35013385b22f9afe", ".githooks/pre-commit": "8299a163efdb520df8726769a7f59a3bc011d8581a7a028e40e4e3c0eb7f52d4", ".github/copilot-instructions.md": "b31f9ba957ce108a83851f48f8dbff8b4301d1c744a77fe76914e6bb8499aca3", - ".github/workflows/new-project-governance.yml": "cc567c87a3a256a3975d93625a575ec6b03cbc4f1b12180abf61f97df35f7de2", + ".github/workflows/new-project-governance.yml": "b41d0df4cb11de5bd1458976e0a1f69ee6733593cd9bb93d68fbbb7fe2e52929", ".governance/AGENT_DECISIONS.md": "fb8fcbb00ba4100ec230aea34ef4634852dae30929eb9eaeafe548fbc7b1de87", ".governance/adoption-bindings.json": "9a6015fde26226d4c55764c81c1ab8e8d42fcf64661533a71d90fcc488edaeb3", ".governance/adoption-bindings.schema.json": "0fbad765ca67924b7b3340dcad09f262b62c979740a291da7dacd8e48d28241c", @@ -42,7 +42,7 @@ ".governance/governance_check.py": "305fc2fbf1c01a8a05e514346aa244977efabedd348379ebffd7e6562949fa17", ".governance/intent.schema.json": "9755f20cd189efe2205629781a6cf6b613bd93e9368416793c4de53256b0a9c0", ".governance/lock.schema.json": "ad80c98f800a4a3310870336dcdaf0aa689cc4988f71084d25d76bea2df1242f", - ".governance/manifest.base.json": "1a834c8de47b3a03eeece19c686966bce6ea5d9fc6a720305dba4ec70d243d8b", + ".governance/manifest.base.json": "763a046bb830631e3a6fb94f6a35403620918a02d4956d7573a8dcd03e4ba1ab", ".governance/manifest.schema.json": "5aa2ccd3f6898834d4e39a78342448145490be56aa132e16ac7c9d64acef8f73", ".governance/package-manifest.json": "a2ea77aca16d59282e1b60105521270ae112ebc52ddb3e08af3eeec26942dc3f", ".governance/precommit_standard_update.py": "c91e2bf9ae9d6ccc77bce0e61450c818a5961edee5bfde3b60426da88e296b0f", @@ -82,7 +82,7 @@ "project/governance-check.sh": "8eb977ff01a96e47455d227ed5ced949eb53ea19537f870d9016f803840e048e", "project/new-ticket.sh": "82353ad45aee1df6764fd8afac5b950027d89507ea82fd3147c469aa9e556247", "project/readme.sh": "b41a9c88374e6de0439284a4561fb11b1b482039bc5ba1bcf6683fd59b1a3968", - "scripts/install-agent-hosts.sh": "0e3a59ed654de872c41f810dd2b7aded96a4bebd862db226ac27ffaf5e478ced", + "scripts/install-agent-hosts.sh": "316a4dd6c877ba8f79a10bb761b188378deb8aea70fb7fcc54a729c664987f44", "scripts/runtime.sh": "8d5c91808d3c126fc84018a12f0b39dd49194fb8b01c5e024932ff9cf2a7a7e2", "wellmanifest_governance.py": "d6b71f091ffd88fb30c96f54162020d9aeb6e54555328834b68ccb21868ecc07", "worktree-guard.yaml": "bea3d3cda9bd764f9e79b975da8f5360df894fdc04fca0407def88ebd49111b7" @@ -92,7 +92,7 @@ "id": "wellmanifest/new-project", "publicationStatus": "published", "sourceRepository": "wellmanifest/new-project", - "sourceRevision": "bf3099667babd14ee778917d911d6c6bad45dcab", - "version": "0.20.12" + "sourceRevision": "824625238d697511f58be2b73c0aa6e15c228a9a", + "version": "0.20.13" } } diff --git a/project/ticket-092/README.md b/project/ticket-092/README.md new file mode 100644 index 0000000..d520711 --- /dev/null +++ b/project/ticket-092/README.md @@ -0,0 +1,13 @@ +# Ticket 092: Published github-script v9 governance + +- **Status**: IN_PROGRESS +- **Workflow state**: PUBLICATION +- **Owner**: codex + +SESSION_EXECUTION_AUTHORIZATION: The user requested continued Semcod publication and implementation. Adopt the published HOME standard package to supersede PR 125, then publish through independent exact-head review. + +## Acceptance criteria +- [x] AC-01: Adopt immutable new-project 0.20.13, preserving target settings and package bindings. +- [ ] AC-02: Managed governance, Compose and Python CI pass before trusted publication; supersede PR 125 after merge. + +Validation: The managed updater verified the published immutable source. Docker engine and Compose checks pass. Governance and required Python CI are publication gates; trusted merge remains pending. diff --git a/project/ticket-092/intent.json b/project/ticket-092/intent.json new file mode 100644 index 0000000..866c436 --- /dev/null +++ b/project/ticket-092/intent.json @@ -0,0 +1,85 @@ +{ + "schema": "new-project.intent/v3", + "ticket": "ticket-092", + "summary": "Adopt published github-script v9 governance", + "workstream": "governance", + "classification": { + "kind": "SERVICE", + "priority": "P2", + "origin": "requested" + }, + "allowedPaths": [ + ".github/workflows/new-project-governance.yml", + ".governance/manifest.base.json", + ".governance/manifest.json", + ".governance/manifest.lock.json", + "scripts/install-agent-hosts.sh", + "pyproject.toml", + "project/ticket-092/**" + ], + "forbiddenPaths": [ + "project/ticket-*/user-*.md" + ], + "stacks": [], + "dependsOn": [], + "conflictsWith": [], + "integrationTicket": null, + "delivery": { + "acceptedBaseSha": "457f5d539131c7ae6705882405e803bd9d0cb585", + "targetBranch": "main", + "outcome": "Adopt published new-project 0.20.13 with github-script v9 and host activation fixes, superseding dependency PR 125.", + "nonGoals": [ + "No product source or permissions or protected review policy changes" + ], + "complexity": "M", + "estimatedMinutes": 60, + "budgets": { + "maxImplementationFiles": 9, + "maxAffectedComponents": 2, + "maxPublicInterfaceChanges": 0, + "maxRuntimeDependencies": 0 + }, + "architecture": { + "status": "accepted", + "decision": "Use the managed immutable standard updater, retaining target-owned manifest settings and package bindings.", + "components": [ + { + "name": "governance", + "paths": [ + ".github/workflows/new-project-governance.yml", + ".governance/manifest.base.json", + ".governance/manifest.json", + ".governance/manifest.lock.json", + "scripts/install-agent-hosts.sh", + "pyproject.toml" + ] + } + ], + "responsibilityChanges": false, + "interfaceChanges": [], + "dataChanges": [], + "ui": { + "impact": "none", + "states": [], + "evidence": [] + }, + "rollback": "Review a subsequent immutable standard update; preserve previous pins in history." + }, + "runtimeDependencies": [], + "standardAdoption": { + "sourceRepository": "wellmanifest/new-project", + "fromRevision": "bf3099667babd14ee778917d911d6c6bad45dcab", + "toRevision": "824625238d697511f58be2b73c0aa6e15c228a9a" + }, + "validation": [ + { + "criterion": "AC-01", + "commands": [ + "./project/governance-check.sh --actor agent", + "docker compose config --quiet" + ], + "evidence": "Managed immutable adoption, governance gate, Compose validation, required Python CI and independent protected review." + } + ] + } +} diff --git a/pyproject.toml b/pyproject.toml index 1ec0256..3f715de 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,8 +71,8 @@ python_files = ["test_*.py"] addopts = "-p wellmanifest_governance" [tool.wellmanifest] -standard = "0.20.12" -revision = "bf3099667babd14ee778917d911d6c6bad45dcab" +standard = "0.20.13" +revision = "824625238d697511f58be2b73c0aa6e15c228a9a" gate = "project/governance-check.sh" [tool.pfix] diff --git a/scripts/install-agent-hosts.sh b/scripts/install-agent-hosts.sh index 9c0ec36..80631d9 100755 --- a/scripts/install-agent-hosts.sh +++ b/scripts/install-agent-hosts.sh @@ -114,18 +114,23 @@ governed = ( | {contract["hook"]["path"]} | set(contract["hook"]["runtimeFiles"]) ) -for item in manifest["files"]: - if item["target"] in governed or item["source"] == contract_source: - print(f"{item['source']}\t{item['target']}\t{int(bool(item['executable']))}") +selected = [item for item in manifest["files"] + if item["target"] in governed or item["source"] == contract_source] +missing = governed - {item["target"] for item in selected} +if missing or not any(item["source"] == contract_source for item in selected): + raise SystemExit("Incomplete host package mapping") +for item in selected: + print(f"{item['source']}\t{item['target']}\t{int(bool(item['executable']))}") PY } activate_in_place() { local dest="$1" dest="$(cd "$dest" && pwd)" - local contract hooks - contract="$(contract_path "$dest")" - hooks="$(hooks_path_config "$contract")" + local contract hooks targets + contract="$(contract_path "$dest")" || return 1 + hooks="$(hooks_path_config "$contract")" || return 1 + targets="$(contract_targets "$contract" "$dest")" || return 1 local missing=() while IFS=$'\t' read -r target is_hook; do @@ -133,10 +138,7 @@ activate_in_place() { missing+=("$target") continue fi - if [[ "$is_hook" == "1" && "$CHECK_ONLY" == false ]]; then - chmod +x "$dest/$target" - fi - done < <(contract_targets "$contract" "$dest") + done <<< "$targets" if [[ "${#missing[@]}" -gt 0 ]]; then printf 'GOV-AGENT-HOST-004: missing host files in %s:\n' "$dest" >&2 @@ -146,6 +148,12 @@ activate_in_place() { fi if [[ "$CHECK_ONLY" == true ]]; then + while IFS=$'\t' read -r target is_hook; do + if [[ "$is_hook" == "1" && ! -x "$dest/$target" ]]; then + echo "GOV-AGENT-HOST-005: hook is not executable: $dest/$target" >&2 + return 1 + fi + done <<< "$targets" local configured; configured="$(git -C "$dest" config --get core.hooksPath || true)" if [[ "$configured" != "$hooks" ]]; then echo "GOV-AGENT-HOST-006: core.hooksPath is '${configured:-unset}', expected '$hooks'" >&2 @@ -155,7 +163,12 @@ activate_in_place() { return 0 fi - git -C "$dest" config core.hooksPath "$hooks" + while IFS=$'\t' read -r target is_hook; do + if [[ "$is_hook" == "1" ]]; then + chmod +x "$dest/$target" || return 1 + fi + done <<< "$targets" + git -C "$dest" config core.hooksPath "$hooks" || return 1 echo "Activated host contract and core.hooksPath=$hooks in $dest" } @@ -166,35 +179,40 @@ bootstrap_into() { echo "Target is not a git work tree: $dest" >&2 exit 1 fi - local contract - contract="$(contract_path "$SOURCE")" + local contract files + contract="$(contract_path "$SOURCE")" || return 1 local manifest="$SOURCE/governance/package-manifest.json" if [[ ! -f "$manifest" ]]; then echo "Source has no governance/package-manifest.json: $SOURCE" >&2 exit 1 fi + files="$(package_host_files "$manifest" "$contract" "${contract#"$SOURCE/"}")" || return 1 + # Validate the entire input before the first destination write. while IFS=$'\t' read -r source target executable; do if [[ ! -f "$SOURCE/$source" ]]; then echo "Source file missing: $SOURCE/$source" >&2 - exit 1 - fi - if [[ "$CHECK_ONLY" == true ]]; then - continue + return 1 fi - mkdir -p "$dest/$(dirname "$target")" - cp -f "$SOURCE/$source" "$dest/$target" + done <<< "$files" + if [[ "$CHECK_ONLY" == true ]]; then + activate_in_place "$dest" + return $? + fi + while IFS=$'\t' read -r source target executable; do + mkdir -p "$dest/$(dirname "$target")" || return 1 + cp -f "$SOURCE/$source" "$dest/$target" || return 1 if [[ "$executable" == "1" ]]; then - chmod +x "$dest/$target" + chmod +x "$dest/$target" || return 1 fi - done < <(package_host_files "$manifest" "$contract" "${contract#"$SOURCE/"}") + done <<< "$files" activate_in_place "$dest" } install_user_files() { - local home="${HOME:-}" - if [[ -z "$home" || ! -d "$home" ]]; then + local task_user_home="${HOME:-}" + if [[ -z "$task_user_home" || ! -d "$task_user_home" ]]; then echo "HOME is not a directory; skipping --user" >&2 return 1 fi @@ -205,13 +223,14 @@ install_user_files() { local rule="$SOURCE/.cursor/rules/new-project-standard.mdc" [[ -f "$rule" ]] || { echo "Source file missing: $rule" >&2; return 1; } - mkdir -p "$home/.cursor/rules" "$home/.gemini" "$home/.claude" "$home/.config/aider" - cp -f "$rule" "$home/.cursor/rules/new-project-standard.mdc" + mkdir -p "$task_user_home/.cursor/rules" "$task_user_home/.gemini" "$task_user_home/.claude" "$task_user_home/.config/aider" || return 1 + cp -f "$rule" "$task_user_home/.cursor/rules/new-project-standard.mdc" || return 1 local marker="wellmanifest/new-project host contract" - for pointer in "$home/.gemini/GEMINI.md" "$home/.claude/CLAUDE.md"; do + for pointer in "$task_user_home/.gemini/GEMINI.md" "$task_user_home/.claude/CLAUDE.md"; do if [[ ! -f "$pointer" ]] || ! grep -Fq "$marker" "$pointer"; then - cat > "$pointer" <> "$pointer" <