Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a3366d4
Add workflow lessons library
justin808 Jul 3, 2026
23ed066
Canonicalize readiness vocabulary across batch skills
justin808 Jul 3, 2026
28adc16
Define shared review finding schema
justin808 Jul 3, 2026
31e99f2
Add autoreview target state helper
justin808 Jul 3, 2026
75f72b5
Add optional task observer skill
justin808 Jul 3, 2026
38a08a9
Document knowledge contracts batch
justin808 Jul 3, 2026
73f2d73
Validate review finding contracts
justin808 Jul 3, 2026
0820890
Address knowledge contract review feedback
justin808 Jul 3, 2026
2d0c2c7
Enforce raw solution date format
justin808 Jul 3, 2026
6df5f29
Address final review portability gaps
justin808 Jul 3, 2026
03aed7f
Fix autoreview host marker after rebase
justin808 Jul 5, 2026
1383bc8
Isolate task observer test environment
justin808 Jul 5, 2026
10e442b
Address final PR 69 review findings
justin808 Jul 5, 2026
309cf11
Address final reviewer edge cases
justin808 Jul 5, 2026
287017b
Address dirty review and credential edge cases
justin808 Jul 5, 2026
1304c66
Clarify task observer private URL errors
justin808 Jul 5, 2026
57249ee
Address final closeout review edge cases
justin808 Jul 5, 2026
c96fb0b
Harden task observer sensitive input filtering
justin808 Jul 5, 2026
2fe6947
Address task observer and validator review hardening
justin808 Jul 5, 2026
59530b0
Catch task observer fragment and token leaks
justin808 Jul 5, 2026
3a4a2e6
Reject ambiguous private URL hosts
justin808 Jul 5, 2026
0d5370a
Address final task observer privacy review
justin808 Jul 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ All notable changes to this portable workflow pack are documented here.

#### Added

- **Add durable workflow solution docs, review finding schema, readiness vocabulary, autoreview target-state fixtures, and the optional `task-observer` skill.**
- **Add `agent-workflows-trust-audit` to check recent merged PRs against `pr-security-preflight` and draft candidate repo-local trust entries for maintainer review.**
- **Add `trusted_metadata_bots` so workflow/status bot comments can be audited as metadata without becoming actionable trusted instructions.**
- **Add `pr-security-preflight --strict-trust` so exact-target batches can report actor-trust findings by default while still supporting fail-closed launches.**
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ the managed-vs-repo-owned boundary, and `--root`/`--only`/`--all` usage.
| `run-ci` | Choose and run repo-local CI checks. |
| `spec` | Turn vague implementation intent into requirements, design, and tasks. |
| `status` | Report tight progress (done/in-progress/blocked/next) without starting new work. |
| `task-observer` | Optionally capture sanitized observations for later skill or workflow improvement review. |
| `tdd` | Drive test-first red-green-refactor loops for features and bug fixes. |
| `triage` | Build a whole-surface issue/PR inventory and batch split. |
| `update-changelog` | Classify merged PRs and update a repo changelog. |
Expand Down
73 changes: 70 additions & 3 deletions bin/install-agent-workflows
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Default targets:
Installed paths:
<target>/skills/*
<target>/workflows/*
<target>/docs/review-finding-schema.md
<target>/docs/solutions/*
<target>/bin/agent-workflow-seam-doctor
<target>/bin/agent-workflows-status
<target>/bin/upgrade-agent-workflows
Expand Down Expand Up @@ -140,11 +142,24 @@ write_metadata() {
mv "$metadata_tmp" "$metadata_path"
}

ensure_real_directory() {
local directory="$1"

if [[ -L "$directory" ]]; then
rm -f "$directory"
fi
if [[ -e "$directory" && ! -d "$directory" ]]; then
echo "Refusing to replace non-directory path: $directory" >&2
exit 1
fi
mkdir -p "$directory"
}

copy_children_preserving_unrelated() {
local source_dir="$1"
local target_dir="$2"
local source_path destination
mkdir -p "$target_dir"
ensure_real_directory "$target_dir"

for source_path in "$source_dir"/*; do
[[ -e "$source_path" ]] || continue
Expand All @@ -154,6 +169,45 @@ copy_children_preserving_unrelated() {
done
}

copy_pack_docs() {
local docs_target="$1"
local schema_destination="$docs_target/review-finding-schema.md"
ensure_real_directory "$docs_target"
ensure_real_directory "$docs_target/solutions"
if [[ -L "$schema_destination" ]]; then
rm -f "$schema_destination"
fi
if [[ -e "$schema_destination" && ! -f "$schema_destination" ]]; then
echo "Refusing to replace non-file path: $schema_destination" >&2
exit 1
fi
install -m 0644 "$repo_root/docs/review-finding-schema.md" "$schema_destination"
copy_children_preserving_unrelated "$repo_root/docs/solutions" "$docs_target/solutions"
}

link_pack_docs() {
local docs_target="$1"
local destination source_path
mkdir -p "$docs_target" "$docs_target/solutions"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

link_pack_docs is the only doc-install path that doesn't use the new ensure_real_directory guard.

Every other path this PR added (copy_pack_docs's calls to ensure_real_directory "$docs_target" / "$docs_target/solutions", and the top-level copy-mode block) converts a pre-existing symlink at the target path into a real directory before writing, or refuses with an explicit error for a non-directory. Here, plain mkdir -p "$docs_target" "$docs_target/solutions" is used instead.

If $docs_target (or its solutions subdir) already exists as a symlink to a directory elsewhere — plausible in a dotfiles-managed home, e.g. ln -s ~/dotfiles/codex-docs ~/.codex/docs before ever running the installer — mkdir -p silently succeeds (it resolves through the symlink) and this function then writes review-finding-schema.md/solutions/* symlinks into that other directory instead of ~/.codex/docs, with no warning. Every other guarded path in this script fails loudly ("Refusing to replace...") in the equivalent situation; this one silently writes to the wrong place.

Suggest using ensure_real_directory here too for consistency (note it would need -L-then-mkdir semantics compatible with symlink mode, or an equivalent explicit-refusal check for the symlink-to-directory case specifically, since ensure_real_directory as written would convert it to a real directory, which is not what symlink mode wants either).


destination="$docs_target/review-finding-schema.md"
if [[ -e "$destination" && ! -L "$destination" ]]; then
echo "Refusing to replace non-symlink path: $destination" >&2
exit 1
fi
ln -sfn "$repo_root/docs/review-finding-schema.md" "$destination"

for source_path in "$repo_root"/docs/solutions/*; do
[[ -e "$source_path" ]] || continue
destination="$docs_target/solutions/$(basename "$source_path")"
if [[ -e "$destination" && ! -L "$destination" ]]; then
echo "Refusing to replace non-symlink path: $destination" >&2
exit 1
fi
ln -sfn "$source_path" "$destination"
done
}
Comment thread
justin808 marked this conversation as resolved.

while [[ $# -gt 0 ]]; do
case "$1" in
--host)
Expand Down Expand Up @@ -201,11 +255,23 @@ host="${resolved%%:*}"
target="${resolved#*:}"

if [[ "$mode" = "copy" ]]; then
mkdir -p "$target/skills" "$target/workflows" "$target/bin"
ensure_real_directory "$target/skills"
ensure_real_directory "$target/workflows"
ensure_real_directory "$target/docs"
ensure_real_directory "$target/bin"
copy_children_preserving_unrelated "$repo_root/skills" "$target/skills"
copy_children_preserving_unrelated "$repo_root/workflows" "$target/workflows"
copy_pack_docs "$target/docs"
for helper in "${bin_helpers[@]}"; do
install -m 0755 "$repo_root/bin/$helper" "$target/bin/$helper"
destination="$target/bin/$helper"
if [[ -L "$destination" ]]; then
rm -f "$destination"
fi
if [[ -e "$destination" && ! -f "$destination" ]]; then
echo "Refusing to replace non-file path: $destination" >&2
exit 1
fi
install -m 0755 "$repo_root/bin/$helper" "$destination"
done
else
mkdir -p "$target/skills" "$target/bin"
Expand All @@ -224,6 +290,7 @@ else
done
rm -f "$target/workflows"
ln -sfn "$repo_root/workflows" "$target/workflows"
link_pack_docs "$target/docs"
for helper in "${bin_helpers[@]}"; do
destination="$target/bin/$helper"
if [[ -e "$destination" && ! -L "$destination" ]]; then
Expand Down
49 changes: 48 additions & 1 deletion bin/install-agent-workflows-test.bash
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ test_codex_host_install_writes_helpers_and_metadata() {
assert_file "$target/skills/pr-batch/SKILL.md"
assert_file "$target/skills/pr-batch/agents/openai.yaml"
assert_file "$target/workflows/pr-processing.md"
assert_file "$target/docs/review-finding-schema.md"
assert_file "$target/docs/solutions/README.md"
assert_file "$target/bin/agent-workflow-seam-doctor"
assert_file "$target/bin/agent-workflows-status"
assert_file "$target/bin/agent-workflows-trust-audit"
Expand Down Expand Up @@ -133,6 +135,8 @@ test_claude_host_install_uses_claude_home_when_target_is_omitted() {
assert_file "$tmp/.claude/skills/pr-batch/SKILL.md"
assert_file "$tmp/.claude/skills/pr-batch/agents/openai.yaml"
assert_file "$tmp/.claude/workflows/pr-processing.md"
assert_file "$tmp/.claude/docs/review-finding-schema.md"
assert_file "$tmp/.claude/docs/solutions/README.md"
assert_file "$tmp/.claude/bin/agent-workflows-status"
assert_file "$tmp/.claude/bin/agent-workflows-trust-audit"
[[ ! -e "$tmp/.claude/.codex-plugin/plugin.json" ]] || fail "Codex native plugin manifest must not be installed into Claude home metadata"
Expand All @@ -142,33 +146,74 @@ test_copy_mode_preserves_unrelated_agent_files() {
local tmp target
tmp="$(mktemp -d)"
target="$tmp/codex-home"
mkdir -p "$target/skills/personal" "$target/workflows" "$target/bin"
mkdir -p "$target/skills/personal" "$target/workflows" "$target/docs" "$target/bin"
printf 'personal skill\n' > "$target/skills/personal/SKILL.md"
printf 'personal workflow\n' > "$target/workflows/personal.md"
printf 'personal docs\n' > "$target/docs/personal.md"
printf '#!/usr/bin/env bash\n' > "$target/bin/personal-helper"

"$ROOT/bin/install-agent-workflows" --host codex --target "$target" >/tmp/install-agent-workflows-test.out

assert_file "$target/skills/personal/SKILL.md"
assert_file "$target/workflows/personal.md"
assert_file "$target/docs/personal.md"
assert_file "$target/docs/review-finding-schema.md"
assert_file "$target/docs/solutions/README.md"
assert_file "$target/bin/personal-helper"
assert_file "$target/skills/pr-batch/SKILL.md"
}

test_copy_mode_does_not_replace_generic_consumer_docs() {
local tmp target
tmp="$(mktemp -d)"
target="$tmp/codex-home"
mkdir -p "$target/docs/adr"
printf 'consumer adoption docs\n' > "$target/docs/adoption.md"
printf 'consumer architecture decision\n' > "$target/docs/adr/0001-consumer.md"

"$ROOT/bin/install-agent-workflows" --host codex --target "$target" >/tmp/install-agent-workflows-test.out

grep -q 'consumer adoption docs' "$target/docs/adoption.md" || fail "copy mode replaced consumer docs/adoption.md"
grep -q 'consumer architecture decision' "$target/docs/adr/0001-consumer.md" || fail "copy mode replaced consumer docs/adr"
assert_file "$target/docs/review-finding-schema.md"
assert_file "$target/docs/solutions/README.md"
}

test_symlink_mode_links_skills_workflows_and_helpers() {
local tmp target
tmp="$(mktemp -d)"
target="$tmp/codex-home"
mkdir -p "$target/docs"
printf 'personal docs\n' > "$target/docs/personal.md"

"$ROOT/bin/install-agent-workflows" --host codex --target "$target" --mode symlink >/tmp/install-agent-workflows-test.out

assert_symlink "$target/skills/pr-batch"
assert_symlink "$target/workflows"
assert_file "$target/docs/personal.md"
assert_symlink "$target/docs/review-finding-schema.md"
[[ -d "$target/docs/solutions" && ! -L "$target/docs/solutions" ]] || fail "expected real docs/solutions directory"
assert_symlink "$target/docs/solutions/README.md"
assert_symlink "$target/bin/agent-workflow-seam-doctor"
assert_symlink "$target/bin/agent-workflows-trust-audit"
assert_file "$target/.agent-workflows-install.json"
}

test_copy_mode_after_symlink_mode_does_not_delete_source_docs() {
local tmp target source_doc
tmp="$(mktemp -d)"
target="$tmp/codex-home"
source_doc="$ROOT/docs/solutions/README.md"

"$ROOT/bin/install-agent-workflows" --host codex --target "$target" --mode symlink >/tmp/install-agent-workflows-test.out
assert_symlink "$target/docs/solutions/README.md"
"$ROOT/bin/install-agent-workflows" --host codex --target "$target" >/tmp/install-agent-workflows-test.out

assert_file "$source_doc"
assert_file "$target/docs/solutions/README.md"
[[ ! -L "$target/docs/solutions/README.md" ]] || fail "copy mode should replace pack doc symlink with a real copy"
}

test_status_reports_not_installed_and_check_failed_explicitly() {
local tmp target output status
tmp="$(mktemp -d)"
Expand Down Expand Up @@ -321,7 +366,9 @@ main() {
test_installed_prompt_guard_ignores_unowned_docs
test_claude_host_install_uses_claude_home_when_target_is_omitted
test_copy_mode_preserves_unrelated_agent_files
test_copy_mode_does_not_replace_generic_consumer_docs
test_symlink_mode_links_skills_workflows_and_helpers
test_copy_mode_after_symlink_mode_does_not_delete_source_docs
test_status_reports_not_installed_and_check_failed_explicitly
test_status_reports_upgrade_available_between_source_commits
test_upgrade_reinstalls_new_source_revision
Expand Down
10 changes: 10 additions & 0 deletions bin/validate
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ ruby bin/agent-workflows-trust-audit-test.rb
echo "== push-downstream unit tests =="
ruby bin/push-downstream-test.rb

echo "== solution docs =="
ruby bin/validate-solutions-test.rb
ruby bin/validate-solutions

echo "== review finding schema =="
ruby bin/validate-review-findings-test.rb
ruby bin/validate-review-findings

echo "== installer/status/upgrade tests =="
bash bin/install-agent-workflows-test.bash

Expand All @@ -71,6 +79,7 @@ echo "== downstream registry dry-run =="
bin/push-downstream --config downstream.yml >/dev/null

echo "== helper tests =="
ruby skills/autoreview/bin/autoreview-target-state-test.rb
ruby skills/address-review/bin/fetch-pr-review-data-test.rb
ruby skills/plan-pr-batch/bin/pr-file-touch-map-test.rb
AGENT_WORKFLOWS_SOURCE_CHECKOUT=1 ruby skills/plan-pr-batch/scripts/check_goal_prompt_size.rb
Expand All @@ -81,6 +90,7 @@ ruby skills/pr-batch/bin/goal-completion-contract-test.rb
ruby skills/pr-batch/bin/pr-ci-readiness-test.rb
ruby skills/pr-batch/bin/agent-coord-bounded-test.rb
ruby skills/pr-batch/bin/pr-security-preflight-test.rb
ruby skills/task-observer/bin/task-observer-test.rb
ruby skills/update-changelog/bin/changelog-merged-prs-test.rb

echo "== rubocop ${RUBOCOP_VERSION} =="
Expand Down
Loading
Loading