diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 0b3b84ca..b090fcdb 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -27,7 +27,7 @@ { "name": "agentic-engineering", "description": "The autonomous engineering system for repository portfolios — engineer, read-only surveyor, and meta-engineer agents; portfolio, product, spend, and improvement workflows; cross-tool instruction architecture and skill discovery; configured by the consumer AGENTS.md", - "version": "6.1.11", + "version": "6.1.12", "source": "./plugins/agentic-engineering" }, { diff --git a/.github/plugin/marketplace.json b/.github/plugin/marketplace.json index 0b3b84ca..b090fcdb 100644 --- a/.github/plugin/marketplace.json +++ b/.github/plugin/marketplace.json @@ -27,7 +27,7 @@ { "name": "agentic-engineering", "description": "The autonomous engineering system for repository portfolios — engineer, read-only surveyor, and meta-engineer agents; portfolio, product, spend, and improvement workflows; cross-tool instruction architecture and skill discovery; configured by the consumer AGENTS.md", - "version": "6.1.11", + "version": "6.1.12", "source": "./plugins/agentic-engineering" }, { diff --git a/plugins/agentic-engineering/.claude-plugin/plugin.json b/plugins/agentic-engineering/.claude-plugin/plugin.json index 5bd4c891..5dcd10e9 100644 --- a/plugins/agentic-engineering/.claude-plugin/plugin.json +++ b/plugins/agentic-engineering/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "agentic-engineering", "description": "The autonomous engineering system for repository portfolios — engineer, read-only surveyor, and meta-engineer agents; portfolio, product, spend, and improvement workflows; cross-tool instruction architecture and skill discovery; configured by the consumer AGENTS.md", - "version": "6.1.11", + "version": "6.1.12", "author": { "name": "devantler-tech", "url": "https://github.com/devantler-tech" diff --git a/plugins/agentic-engineering/CHANGELOG.md b/plugins/agentic-engineering/CHANGELOG.md index 6d330713..8d9073cd 100644 --- a/plugins/agentic-engineering/CHANGELOG.md +++ b/plugins/agentic-engineering/CHANGELOG.md @@ -21,6 +21,10 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and new one before re-enabling unattended writes. Stopping early resumes writes with the retired FinOps schedule still armed, or with a schedule pointing at an entrypoint that no longer resolves. +## 6.1.12 — 2026-10-05 + +**Changed** — sync `product-engineering` from `https://github.com/devantler-tech/agent-skills` at `refs/tags/v1.21.31`. + ## 6.1.11 — 2026-10-05 **Changed** — sync `agent-improvement` from `https://github.com/devantler-tech/agent-skills` at `refs/tags/v1.21.29`. diff --git a/plugins/agentic-engineering/plugin.json b/plugins/agentic-engineering/plugin.json index 5bd4c891..5dcd10e9 100644 --- a/plugins/agentic-engineering/plugin.json +++ b/plugins/agentic-engineering/plugin.json @@ -1,7 +1,7 @@ { "name": "agentic-engineering", "description": "The autonomous engineering system for repository portfolios — engineer, read-only surveyor, and meta-engineer agents; portfolio, product, spend, and improvement workflows; cross-tool instruction architecture and skill discovery; configured by the consumer AGENTS.md", - "version": "6.1.11", + "version": "6.1.12", "author": { "name": "devantler-tech", "url": "https://github.com/devantler-tech" diff --git a/plugins/agentic-engineering/skills/product-engineering/SKILL.md b/plugins/agentic-engineering/skills/product-engineering/SKILL.md index 28f4aee6..a5ed0705 100644 --- a/plugins/agentic-engineering/skills/product-engineering/SKILL.md +++ b/plugins/agentic-engineering/skills/product-engineering/SKILL.md @@ -3,9 +3,9 @@ description: 'The ADVANCE playbook for an autonomous AI engineer — how to move license: Apache-2.0 metadata: github-path: product-engineering - github-ref: refs/tags/v1.21.27 + github-ref: refs/tags/v1.21.31 github-repo: https://github.com/devantler-tech/agent-skills - github-tree-sha: 32ad063f85e506accbcd1ec661aabb8263f58af3 + github-tree-sha: 3f214543fb26f041a047f0843e88ade53f2e91cd name: product-engineering --- # Product engineering — moving products forward diff --git a/plugins/agentic-engineering/skills/product-engineering/references/tool-maturation.md b/plugins/agentic-engineering/skills/product-engineering/references/tool-maturation.md index db082b92..8558cd75 100644 --- a/plugins/agentic-engineering/skills/product-engineering/references/tool-maturation.md +++ b/plugins/agentic-engineering/skills/product-engineering/references/tool-maturation.md @@ -45,9 +45,13 @@ working tree is dirty, without checking out or executing its files. Its conserva `tracked-shell-paths-v1` includes every tracked regular `*.sh` path except `*.test.sh`; it does not apply a size threshold or decide which files are non-trivial. Each path carries its committed blob identifier and executable bit. Before selecting files, it compares the complete tree listing with -an independent raw diff from Git's empty tree to the named commit. Missing complete records, +an independent raw diff from Git's empty tree to the named commit. Each reachable tree object's +original bytes must also pass Git's canonical component validation and retain their committed +identity; agreement between flattened paths alone cannot establish a valid tree. Missing complete records, disagreement between those views, tree/object read failures, selected symlinks and paths that cannot be represented faithfully in UTF-8 JSON produce exit `2` (`UNKNOWN`) with no success payload. +Git observations also suppress configured filesystem-monitor callbacks without changing repository +configuration, so a native tree read cannot silently execute a caller-provided hook. To include Go entrypoint evidence, add `--include-go` to the same invocation. This opt-in needs Go 1.22 or later and compiles only the installed `scripts/go-entrypoint.go` parser, with modules, diff --git a/plugins/agentic-engineering/skills/product-engineering/scripts/inspect-shell-helpers.sh b/plugins/agentic-engineering/skills/product-engineering/scripts/inspect-shell-helpers.sh index 8a142afa..82123517 100644 --- a/plugins/agentic-engineering/skills/product-engineering/scripts/inspect-shell-helpers.sh +++ b/plugins/agentic-engineering/skills/product-engineering/scripts/inspect-shell-helpers.sh @@ -43,7 +43,7 @@ export GIT_NO_LAZY_FETCH=1 unset GIT_DIR GIT_WORK_TREE GIT_COMMON_DIR GIT_INDEX_FILE GIT_OBJECT_DIRECTORY \ GIT_ALTERNATE_OBJECT_DIRECTORIES GIT_NAMESPACE # Explicit no-fetch capability is required; unsupported Git refuses before reads. -read_git() { git --no-lazy-fetch --no-replace-objects --no-optional-locks -C "$repo" "$@"; } +read_git() { git -c core.fsmonitor=false --no-lazy-fetch --no-replace-objects --no-optional-locks -C "$repo" "$@"; } top=$(read_git rev-parse --show-toplevel 2>/dev/null && printf '.') || unknown 'not a worktree repository' top=${top%$'\n.'} top=$(cd "$top" && pwd -P && printf '.') || unknown 'repository root is unavailable' @@ -69,6 +69,9 @@ read_git diff-tree -r -t --raw -z --no-abbrev --no-commit-id --no-renames \ encode_record() { printf '%s %s\t%s\0' "$1" "$2" "$3" | base64 | tr -d '\r\n' || return; printf '\n'; } : > "$tmp/tree-records" : > "$tmp/tree-paths" +root_tree=$(read_git rev-parse --verify "$revision^{tree}" 2>/dev/null) || unknown 'root tree identity is unavailable' +[[ $root_tree =~ ^([0-9a-f]{40}|[0-9a-f]{64})$ ]] || unknown 'root tree identity is invalid' +printf '%s\n' "$root_tree" > "$tmp/tree-objects" entry= while IFS= read -r -d '' entry; do [[ $entry == *$'\t'* ]] || unknown 'malformed tree entry' @@ -80,10 +83,20 @@ while IFS= read -r -d '' entry; do 040000:tree|100644:blob|100755:blob|120000:blob|160000:commit) : ;; *) unknown 'unsupported tree metadata' ;; esac + [[ $type != tree ]] || printf '%s\n' "$blob" >> "$tmp/tree-objects" encode_record "$mode" "$blob" "$path" >> "$tmp/tree-records" || unknown 'tree record could not be encoded' encode_record '' '' "$path" >> "$tmp/tree-paths" || unknown 'tree path could not be encoded' done < "$tmp/tree" [[ -z $entry ]] || unknown 'committed tree has an unterminated record' +# Flattened listings cannot distinguish nested paths from malformed tree components. +# Git parses each original tree without writing objects; bind the complete raw read +# back to its immutable identity before accepting any inventory. +LC_ALL=C sort -u "$tmp/tree-objects" > "$tmp/unique-tree-objects" || unknown 'tree objects could not be compared' +while IFS= read -r tree_object; do + read_git cat-file tree "$tree_object" > "$tmp/raw-tree" 2>/dev/null || unknown 'original tree object could not be read' + observed_tree=$(read_git hash-object -t tree --stdin < "$tmp/raw-tree" 2>/dev/null) || unknown 'original tree components are not canonical' + [[ $observed_tree == "$tree_object" ]] || unknown 'original tree bytes do not match the committed identity' +done < "$tmp/unique-tree-objects" : > "$tmp/diff-records" header= while IFS= read -r -d '' header; do diff --git a/plugins/agentic-engineering/skills/product-engineering/scripts/inspect-shell-observation.test.sh b/plugins/agentic-engineering/skills/product-engineering/scripts/inspect-shell-observation.test.sh new file mode 100644 index 00000000..0df57bd5 --- /dev/null +++ b/plugins/agentic-engineering/skills/product-engineering/scripts/inspect-shell-observation.test.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# Configured observation callbacks must stay inert, including at valid trees. +set -euo pipefail +unset GIT_DIR GIT_WORK_TREE GIT_COMMON_DIR GIT_INDEX_FILE GIT_OBJECT_DIRECTORY \ + GIT_ALTERNATE_OBJECT_DIRECTORIES GIT_NAMESPACE GIT_PREFIX +here=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +work=$(mktemp -d) +trap 'rm -rf "$work"' EXIT +git init -q "$work/repo" +git -C "$work/repo" config user.name 'Observation fixture' +git -C "$work/repo" config user.email fixture@example.invalid +printf '#!/bin/sh\nexit 0\n' > "$work/repo/helper.sh" +git -C "$work/repo" add helper.sh +git -C "$work/repo" -c commit.gpgsign=false commit -qm fixture +revision=$(git -C "$work/repo" rev-parse HEAD) +printf '#!/bin/sh\nprintf executed > "%s"\nprintf "token\\n"\n' "$work/marker" > "$work/hook" +chmod +x "$work/hook" +git -C "$work/repo" config core.fsmonitor "$work/hook" +# Snapshot raw caller files, without an index-consulting observation of our own. +snapshot() { + shasum "$work/repo/.git/index" "$work/repo/.git/config" + find "$work/repo/.git/refs" "$work/repo/.git/objects" -type f -exec shasum {} + | LC_ALL=C sort +} +snapshot > "$work/before" +pass=0 fail=0 +record() { + if "$@"; then printf 'PASS: %s\n' "$label"; pass=$((pass+1)) + else printf 'FAIL: %s\n' "$label"; fail=$((fail+1)); fi +} +bash "$here/inspect-shell-helpers.sh" --repo-dir "$work/repo" --revision "$revision" > "$work/disabled" +label='disabled inventory retains no execution authority' +record jq -e '.status=="DISABLED" and .authority=="NONE"' "$work/disabled" +label='disabled inventory never runs the configured hook'; record test ! -e "$work/marker" +bash "$here/inspect-shell-helpers.sh" --inspect --repo-dir "$work/repo" --revision "$revision" > "$work/enabled" +label='enabled inventory retains its exact committed helper' +record jq -e '.status=="OBSERVED" and .authority=="NONE" and .coverage.selectedPaths==1 and .candidates[0].path=="helper.sh"' "$work/enabled" +label='enabled inventory never runs the configured hook'; record test ! -e "$work/marker" +snapshot > "$work/after" +label='observations retain index, configuration, refs and objects'; record cmp "$work/before" "$work/after" +# The harmless callback is executable and its marker would detect execution. +"$work/hook" > "$work/control" +label='configured marker callback is an effective control'; record test -s "$work/marker" +printf 'Observation controls: %s pass, %s fail\n' "$pass" "$fail" +[[ $fail == 0 ]] diff --git a/plugins/agentic-engineering/skills/product-engineering/scripts/inspect-shell-tree-integrity.test.sh b/plugins/agentic-engineering/skills/product-engineering/scripts/inspect-shell-tree-integrity.test.sh new file mode 100644 index 00000000..6b80b218 --- /dev/null +++ b/plugins/agentic-engineering/skills/product-engineering/scripts/inspect-shell-tree-integrity.test.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +# Canonical tree components must be observed before flattened paths can prove coverage. +set -euo pipefail +unset GIT_DIR GIT_WORK_TREE GIT_COMMON_DIR GIT_INDEX_FILE GIT_OBJECT_DIRECTORY \ + GIT_ALTERNATE_OBJECT_DIRECTORIES GIT_NAMESPACE GIT_PREFIX +here=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +work=$(mktemp -d) +trap 'rm -rf "$work"' EXIT +pass=0 fail=0 +# Preserve all results so malformed and valid controls run against the same object store. +record() { + if "$@"; then printf 'PASS: %s\n' "$label"; pass=$((pass + 1)) + else printf 'FAIL: %s\n' "$label"; fail=$((fail + 1)); fi +} +git init -q "$work/repo" +git -C "$work/repo" config user.name 'Tree integrity fixture' +git -C "$work/repo" config user.email fixture@example.invalid +git -C "$work/repo" config commit.gpgsign false +blob=$(printf 'do not execute\n' | git -C "$work/repo" hash-object -w --stdin) +# Write Git's binary tree representation directly; mktree refuses malformed components. +raw_tree() { + local mode=$1 name=$2 object=$3 i + printf '%s %s\0' "$mode" "$name" + for ((i=0; i<${#object}; i+=2)); do printf '%b' "\\x${object:i:2}"; done +} +# Invoke the real installed observer and retain both streams and the native exit code. +observe() { + status=0 + bash "$here/inspect-shell-helpers.sh" --inspect --repo-dir "$work/repo" \ + --revision "$1" > "$work/out" 2> "$work/error" || status=$? +} +# UNKNOWN must never carry a partial success report. +refused() { [[ $status == 2 && ! -s $work/out ]] && grep -q '^UNKNOWN:' "$work/error"; } +# A nested path remains legitimate even though its flattened spelling contains a slash. +raw_tree 100644 $'quoted" tab\tline\n.sh' "$blob" > "$work/nested.tree" +nested=$(git -C "$work/repo" hash-object -t tree -w "$work/nested.tree") +printf '040000 tree %s\tfolder\0' "$nested" | git -C "$work/repo" mktree -z > "$work/root" +root=$(cat "$work/root") +valid=$(printf 'valid nested fixture\n' | git -C "$work/repo" commit-tree "$root") +observe "$valid" +label='nested whitespace path retains exact committed identity' +# shellcheck disable=SC2016 # These variables belong to jq. +record jq -e --arg path $'folder/quoted" tab\tline\n.sh' --arg blob "$blob" \ + '.status=="OBSERVED" and .coverage.selectedPaths==1 and .candidates[0].path==$path and .candidates[0].blob==$blob' "$work/out" + +for name in ../escape.sh folder/slashed.sh . .. .git; do + raw_tree 100644 "$name" "$blob" > "$work/raw.tree" + tree=$(git -C "$work/repo" hash-object --literally -t tree -w "$work/raw.tree") + revision=$(printf 'malformed component fixture\n' | git -C "$work/repo" commit-tree "$tree") + observe "$revision" + label="malformed component refuses: $name" + record refused + # Every reachable subtree needs validation, including nonselected components. + printf '040000 tree %s\tnested\0' "$tree" | git -C "$work/repo" mktree -z > "$work/nested-root" + revision=$(printf 'nested malformed fixture\n' | git -C "$work/repo" commit-tree "$(cat "$work/nested-root")") + observe "$revision" + label="nested malformed component refuses: $name" + record refused +done +observe "$valid" +label='unrelated malformed objects do not invalidate the selected valid tree' +record jq -e '.status=="OBSERVED" and .coverage.selectedPaths==1' "$work/out" + +# Failed and substituted raw-object reads cannot masquerade as complete tree inspection. +real_git=$(command -v git) +mkdir "$work/bin" +cat > "$work/bin/git" <<'STUB' +#!/usr/bin/env bash +if [[ " $* " == *' cat-file tree '* ]]; then + if [[ $MODE == partial ]]; then "$REAL_GIT" "$@"; exit 73; fi + exit 0 +fi +exec "$REAL_GIT" "$@" +STUB +chmod +x "$work/bin/git" +for mode in partial substituted; do + MODE=$mode REAL_GIT=$real_git PATH="$work/bin:$PATH" observe "$valid" + label="$mode raw-tree read refuses without success" + record refused +done +printf 'Tree integrity controls: %s pass, %s fail\n' "$pass" "$fail" +[[ $fail == 0 ]]