You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iggy.apache.org by hg...@apache.org on 2026/10/02 12:18:19 UTC
(iggy) branch chore/shared-agent-skills updated (54a0976b5 -> 68122f534)
This is an automated email from the ASF dual-hosted git repository.
hubcio pushed a change to branch chore/shared-agent-skills
in repository https://gitbox.apache.org/repos/asf/iggy.git
omit 54a0976b5 chore(repo): make repository skills available to Codex
add 5c09a0580 chore(deps): Bump the java group across 3 directories with 7 updates (#4352)
add 282b5af18 feat(server): guard against running out of file descriptors (#4318)
add 8f93cad89 fix(server)!: make logging honor retention, level, path and file_enabled (#4354)
add 6fe34888d feat(gateways): answer Kafka Fetch with records from Iggy (#4336)
add 8121e7e2b chore(deps): Bump urllib3 from 2.7.0 to 2.8.0 in /foreign/python (#4366)
add ed6e6e716 fix(server): prevent panic when the cleaner removes expired tokens (#4364)
add a08d0ca8f test(sdk): reproduce client lifecycle bugs (#4344)
add 985b0ac1f chore(deps): Bump brace-expansion from 5.0.9 to 5.0.12 in /foreign/node (#4358)
add 88c842ccc chore(deps): Bump brace-expansion from 5.0.9 to 5.0.12 in /web (#4359)
add fc927ab28 chore(deps): Bump the web group across 1 directory with 8 updates (#4375)
add 73f1f7023 chore(deps): Bump the node group across 2 directories with 6 updates (#4376)
add db581c276 chore(deps): Bump brace-expansion from 5.0.9 to 5.0.12 in /examples/node (#4377)
add a6bcfd694 chore(deps): Bump devalue from 5.9.2 to 5.9.4 in /web (#4378)
add d7458b8e6 test(go): cover offset, partition, and consumer group command serialization (#4370)
add e4ff95b88 feat(go): add GetMe to the Go SDK (#4371)
add 071121c70 fix(consensus): preserve recovery floors and reject aged-out requests (#4365)
new 7b4378203 chore(repo): make repository skills available to Codex
new 68122f534 fix(ci): close skill description gaps and fix repair hints
This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version. This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:
* -- * -- B -- O -- O -- O (54a0976b5)
\
N -- N -- N refs/heads/chore/shared-agent-skills (68122f534)
You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.
Any revisions marked "omit" are not gone; other references still
refer to them. Any revisions marked "discard" are gone forever.
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.config/nextest.toml | 3 +-
.github/actions/go/pre-merge/action.yml | 6 +-
.pre-commit-config.yaml | 6 +-
Cargo.lock | 1 +
bdd/java/build.gradle.kts | 4 +-
.../src/responses/system/get_stats.rs | 46 +-
core/cli/src/commands/binary_system/stats.rs | 10 +
core/common/src/error/iggy_error.rs | 23 +
core/common/src/traits/partition_client.rs | 5 +
core/common/src/traits/topic_client.rs | 5 +
core/common/src/types/stats/mod.rs | 11 +
core/common/src/wire_conversions.rs | 2 +
core/configs/src/common/defaults.rs | 11 +
core/configs/src/common/displays.rs | 5 +-
core/configs/src/common/system.rs | 5 +
core/configs/src/common/validators.rs | 101 +-
core/configs/src/server_config/defaults.rs | 2 +
core/configs/src/server_config/displays.rs | 13 +-
core/configs/src/server_config/message_bus.rs | 11 +
core/configs/src/server_config/metadata.rs | 25 +-
core/configs/src/server_config/server.rs | 21 +
core/consensus/src/client_table.rs | 286 +-
core/consensus/src/fatal.rs | 60 -
core/consensus/src/impls.rs | 446 ++-
core/consensus/src/lib.rs | 5 +-
core/consensus/src/observability.rs | 36 +
core/integration/src/harness/config/server.rs | 5 +
core/integration/src/harness/handle/server.rs | 67 +-
.../tests/cli/system/test_stats_command.rs | 22 +-
core/integration/tests/cluster/partition_dedup.rs | 11 +
core/integration/tests/sdk/client_lifecycle.rs | 193 ++
core/integration/tests/sdk/mod.rs | 1 +
.../tests/server/connections_limit_vsr.rs | 115 +
.../tests/server/descriptor_exhaustion_vsr.rs | 271 ++
core/integration/tests/server/mod.rs | 8 +
.../tests/server/partition_view_durability_vsr.rs | 8 +-
.../tests/server/partitions_limit_vsr.rs | 121 +
.../server/personal_access_token_cleaner_vsr.rs | 84 +
.../server/scenarios/log_rotation_scenario.rs | 90 +
core/integration/tests/server/stats_vsr.rs | 26 +-
core/journal/src/durable_storage.rs | 21 +-
core/journal/src/file_storage.rs | 15 +-
core/journal/src/partition_journal.rs | 8 +-
core/journal/src/prepare_journal.rs | 302 ++-
core/journal/src/superblock.rs | 16 +-
core/message_bus/src/accept.rs | 89 +
core/message_bus/src/client_listener/mod.rs | 4 +-
core/message_bus/src/client_listener/tcp.rs | 2 +
core/message_bus/src/client_listener/tcp_tls.rs | 2 +
core/message_bus/src/client_listener/ws.rs | 2 +
core/message_bus/src/client_listener/wss.rs | 2 +
core/message_bus/src/connection_cap.rs | 162 ++
core/message_bus/src/installer/mod.rs | 51 +-
core/message_bus/src/installer/quic.rs | 1 +
core/message_bus/src/installer/tcp.rs | 22 +-
core/message_bus/src/installer/tcp_tls.rs | 4 +-
core/message_bus/src/installer/ws.rs | 4 +-
core/message_bus/src/installer/wss.rs | 4 +-
core/message_bus/src/lib.rs | 9 +-
core/message_bus/src/replica/listener.rs | 8 +-
core/message_bus/tests/common/mod.rs | 18 +-
core/message_bus/tests/connection_cap.rs | 100 +
core/message_bus/tests/duplicate_client_id.rs | 10 +-
core/message_bus/tests/installer_panic_cleanup.rs | 3 +
core/metadata/src/impls/metadata.rs | 545 +++-
core/metadata/src/stm/authz.rs | 52 +-
core/metadata/src/stm/stream.rs | 14 +
core/partitions/src/iggy_index_reader.rs | 11 +-
core/partitions/src/iggy_index_writer.rs | 2 +
core/partitions/src/iggy_partition.rs | 291 +-
core/partitions/src/install_backup.rs | 57 +-
core/partitions/src/lib.rs | 4 +-
core/partitions/src/messages_writer.rs | 2 +
core/partitions/src/offset_recovery.rs | 128 +-
core/partitions/src/offset_storage.rs | 11 +-
core/partitions/src/partition_storage.rs | 323 ++-
core/partitions/src/segment_anchor.rs | 5 +-
core/partitions/src/segment_recovery.rs | 177 +-
core/partitions/src/state_transfer.rs | 114 +-
core/sdk/src/clients/producer.rs | 242 +-
core/sdk/src/http/http_client.rs | 15 +-
core/sdk/src/vsr.rs | 28 +-
core/server/README.md | 62 +
core/server/config.toml | 40 +-
core/server/src/boot/fd_limit.rs | 204 ++
core/server/src/boot/listeners.rs | 45 +-
core/server/src/boot/mod.rs | 51 +-
core/server/src/boot/recovery.rs | 10 +-
core/server/src/boot/threads.rs | 2 +
core/server/src/consumer_group.rs | 4 +-
core/server/src/dispatch/host.rs | 3 +
core/server/src/dispatch/mod.rs | 7 +-
core/server/src/dispatch/reads.rs | 2 +-
core/server/src/dispatch/test_support.rs | 17 +-
core/server/src/http.rs | 74 +-
core/server/src/http/extractor.rs | 2 +-
core/server/src/http/handlers.rs | 2 +-
core/server/src/http/reads.rs | 3 +-
core/server/src/http/session.rs | 7 +-
core/server/src/http/state.rs | 43 +-
core/server/src/http/submit.rs | 6 +-
core/server/src/http/tls.rs | 50 +-
core/server/src/lib.rs | 1 +
core/server/src/main.rs | 35 +-
core/server/src/partition_helpers.rs | 900 ++++++-
core/server/src/partition_reconciler.rs | 84 +-
core/server/src/responses.rs | 4 +-
core/server/src/server_error.rs | 26 +
core/server/src/session_manager.rs | 9 +
core/server/src/snapshot.rs | 13 +-
core/server/src/sysinfo_printer.rs | 208 ++
core/server/src/sysinfo_probe.rs | 134 +-
core/server_common/Cargo.toml | 1 +
core/server_common/src/fatal.rs | 237 ++
core/server_common/src/lib.rs | 5 +-
core/server_common/src/log/logger.rs | 294 +-
core/server_common/src/log/mod.rs | 2 +-
core/server_common/src/log/settings.rs | 124 +
.../src/segment_storage/index_reader.rs | 26 +-
.../src/segment_storage/index_writer.rs | 103 -
.../src/segment_storage/messages_reader.rs | 4 +
.../src/segment_storage/messages_writer.rs | 120 -
core/server_common/src/segment_storage/mod.rs | 122 +-
core/shard/src/builder.rs | 1 +
core/shard/src/coordinator.rs | 468 ++--
core/shard/src/lib.rs | 111 +-
core/shard/src/router.rs | 34 +-
core/simulator/src/bus.rs | 15 +-
core/simulator/src/lib.rs | 326 ++-
core/system_stats/src/lib.rs | 86 +-
examples/java/build.gradle.kts | 4 +-
examples/node/package-lock.json | 26 +-
examples/node/package.json | 4 +-
foreign/cpp/include/iggy.hpp | 22 +-
foreign/cpp/src/lib.rs | 4 +
foreign/cpp/src/type_conversion.rs | 2 +
foreign/cpp/src/type_conversions.cpp | 3 +-
foreign/cpp/tests/e2e/client.cpp | 2 +
.../Iggy_SDK.Tests.Integration/SystemTests.cs | 1 +
foreign/csharp/Iggy_SDK/Contracts/StatsResponse.cs | 10 +
.../VsrRequestOutcomeUnknownException.cs | 9 +-
.../Implementations/HttpMessageStream.cs | 9 +-
.../Implementations/TcpMessageStream.Vsr.cs | 13 +-
foreign/csharp/Iggy_SDK/Mappers/BinaryMapper.cs | 22 +-
.../Publishers/BackgroundMessageProcessor.cs | 5 +-
foreign/csharp/Iggy_SDK/Vsr/ConsensusSession.cs | 4 +-
foreign/csharp/Iggy_SDK/Vsr/VsrError.cs | 1 +
.../ClientTests/HttpTopicOptionsTests.cs | 19 +
.../Iggy_SDK_Tests/MapperTests/BinaryMapper.cs | 28 +
.../Iggy_SDK_Tests/UtilityTests/SendUnitTests.cs | 24 +
.../csharp/Iggy_SDK_Tests/Utils/BinaryFactory.cs | 67 +-
.../Iggy_SDK_Tests/Utils/Stats/StatsFactory.cs | 7 +-
.../VsrTests/EndpointFailoverTests.cs | 45 +
foreign/go/client/tcp/tcp_clients_management.go | 9 +
foreign/go/contracts/client.go | 4 +
foreign/go/contracts/stats.go | 8 +
foreign/go/contracts/stats_test.go | 39 +-
foreign/go/errors/errors.yaml | 8 +
foreign/go/errors/errors_gen.go | 34 +
foreign/go/errors/errors_test.go | 6 +
foreign/go/internal/command/consumer_group_test.go | 130 +
foreign/go/internal/command/offset_test.go | 120 +
foreign/go/internal/command/partition_test.go | 89 +
foreign/go/internal/command/system.go | 12 +
foreign/go/internal/command/system_test.go | 1 +
foreign/go/internal/vsr/envelope.go | 6 +-
foreign/go/tests/e2e_test.go | 38 +-
foreign/java/gradle/libs.versions.toml | 8 +-
.../apache/iggy/client/async/tcp/PollRouter.java | 32 +-
.../client/async/tcp/vsr/VsrRequestEncoder.java | 5 +-
.../org/apache/iggy/exception/IggyErrorCode.java | 7 +
.../org/apache/iggy/serde/BytesDeserializer.java | 17 +-
.../main/java/org/apache/iggy/system/Stats.java | 9 +-
.../AsyncIggyTcpClientTransientFailoverTest.java | 57 +-
.../iggy/client/blocking/SystemClientBaseTest.java | 2 +
.../client/blocking/http/ObjectMapperTest.java | 51 +-
.../apache/iggy/exception/IggyErrorCodeTest.java | 2 +
.../apache/iggy/serde/BytesDeserializerTest.java | 41 +
foreign/node/package-lock.json | 321 +--
foreign/node/package.json | 6 +-
foreign/node/src/e2e/tcp.parallel.e2e.ts | 5 +-
foreign/node/src/e2e/tcp.system.e2e.ts | 5 +-
foreign/node/src/wire/error.code.test.ts | 11 +
foreign/node/src/wire/error.code.ts | 2 +
.../node/src/wire/system/get-stats.command.test.ts | 127 +
foreign/node/src/wire/system/get-stats.command.ts | 127 +-
foreign/node/src/wire/vsr/index.ts | 5 +-
foreign/python/apache_iggy.pyi | 16 +
foreign/python/src/stats.rs | 20 +
foreign/python/tests/test_stats.py | 4 +
foreign/python/uv.lock | 6 +-
.../swift/Sources/Iggy/Errors/IggyErrorCode.swift | 4 +
foreign/swift/Sources/Iggy/Models/Resources.swift | 8 +-
foreign/swift/Tests/IggyTests/ErrorCodeTests.swift | 1 +
foreign/swift/Tests/IggyTests/Fixtures/golden.json | 4 +-
foreign/swift/Tools/golden-vectors/src/main.rs | 2 +
gateways/kafka/Cargo.toml | 2 +
gateways/kafka/README.md | 147 +-
gateways/kafka/docs/ACL_MAPPING.md | 6 +-
gateways/kafka/docs/AUTHENTICATION.md | 10 +-
gateways/kafka/docs/BRIDGE_MAPPING.md | 10 +-
gateways/kafka/docs/CONSUMER_GROUPS.md | 4 +-
gateways/kafka/docs/IDEMPOTENCE.md | 22 +-
gateways/kafka/docs/MANUAL_TESTING.md | 2 +-
gateways/kafka/docs/SCOPE.md | 22 +-
gateways/kafka/docs/TEST_SUITE.md | 6 +-
gateways/kafka/src/bridge/error.rs | 39 +-
gateways/kafka/src/bridge/iggy_bridge/fetch.rs | 468 +++-
gateways/kafka/src/bridge/iggy_bridge/mod.rs | 90 +-
gateways/kafka/src/bridge/iggy_bridge/offsets.rs | 141 +-
gateways/kafka/src/bridge/iggy_bridge/produce.rs | 33 +-
gateways/kafka/src/bridge/mod.rs | 4 +-
gateways/kafka/src/protocol/api.rs | 57 +-
.../kafka/src/protocol/handlers/create_topics.rs | 17 +-
gateways/kafka/src/protocol/handlers/fetch.rs | 2830 +++++++++++++++++++-
.../kafka/src/protocol/handlers/list_offsets.rs | 120 +-
gateways/kafka/src/protocol/handlers/metadata.rs | 14 +-
gateways/kafka/src/protocol/handlers/mod.rs | 82 +-
gateways/kafka/src/protocol/handlers/produce.rs | 57 +-
gateways/kafka/src/protocol/mod.rs | 1 +
gateways/kafka/src/protocol/probe_board.rs | 468 ++++
gateways/kafka/src/records.rs | 110 +-
gateways/kafka/src/server.rs | 21 +-
gateways/kafka/tests/fetch_real_bridge_tests.rs | 821 ++++++
gateways/kafka/tests/kafka_client_e2e_tests.rs | 211 +-
scripts/ci/skills.sh | 13 +-
web/package-lock.json | 379 +--
web/package.json | 18 +-
228 files changed, 14545 insertions(+), 2598 deletions(-)
delete mode 100644 core/consensus/src/fatal.rs
create mode 100644 core/integration/tests/sdk/client_lifecycle.rs
create mode 100644 core/integration/tests/server/connections_limit_vsr.rs
create mode 100644 core/integration/tests/server/descriptor_exhaustion_vsr.rs
create mode 100644 core/integration/tests/server/partitions_limit_vsr.rs
create mode 100644 core/integration/tests/server/personal_access_token_cleaner_vsr.rs
create mode 100644 core/message_bus/src/accept.rs
create mode 100644 core/message_bus/src/connection_cap.rs
create mode 100644 core/message_bus/tests/connection_cap.rs
create mode 100644 core/server/src/boot/fd_limit.rs
create mode 100644 core/server/src/sysinfo_printer.rs
create mode 100644 core/server_common/src/fatal.rs
delete mode 100644 core/server_common/src/segment_storage/index_writer.rs
delete mode 100644 core/server_common/src/segment_storage/messages_writer.rs
create mode 100644 foreign/go/internal/command/consumer_group_test.go
create mode 100644 foreign/go/internal/command/offset_test.go
create mode 100644 foreign/go/internal/command/partition_test.go
create mode 100644 foreign/node/src/wire/system/get-stats.command.test.ts
create mode 100644 gateways/kafka/src/protocol/probe_board.rs
create mode 100644 gateways/kafka/tests/fetch_real_bridge_tests.rs
(iggy) 02/02: fix(ci): close skill description gaps and fix repair hints
Posted by hg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
hubcio pushed a commit to branch chore/shared-agent-skills
in repository https://gitbox.apache.org/repos/asf/iggy.git
commit 68122f534c2e82d1bcdb7284655916df4d5ef9f5
Author: Hubert Gruszecki <h....@gmail.com>
AuthorDate: Fri Oct 2 14:05:04 2026 +0200
fix(ci): close skill description gaps and fix repair hints
skills.sh took `description: ""` as non-empty, and it read a block
header such as `|2` or `| # note` as the description text, so an
over-long description passed. The `ln -s` hint did not replace a
wrong link. It failed, or it put the new link inside the skill
that the wrong link pointed to. The invocation errors did not name
the files to edit.
Strip one pair of quotes before the tests, accept the full block
header grammar, print `ln -sfn`, and print both file paths.
---
scripts/ci/skills.sh | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/scripts/ci/skills.sh b/scripts/ci/skills.sh
index e7e0c94f6..7124a763a 100755
--- a/scripts/ci/skills.sh
+++ b/scripts/ci/skills.sh
@@ -74,9 +74,12 @@ for f in "${files[@]}"; do
has_name=1
}
state==1 && /^description:[[:space:]]/ {
- desc=$0; sub(/^description:[[:space:]]*/, "", desc);
+ desc=$0; sub(/^description:[[:space:]]*/, "", desc); sub(/[[:space:]]+$/, "", desc)
has_desc=1
- if (desc ~ /^[|>][-+]?$/) { desc=""; in_desc=1; next }
+ # Block scalar header: indentation (1-9) and chomping (+/-) in either order, then a comment.
+ if (desc ~ /^[|>]([-+]?[1-9]?|[1-9][-+])([[:space:]]+#.*)?$/) { desc=""; in_desc=1; next }
+ # Measure a quoted value without its quotes, so "" is empty.
+ if (desc ~ /^".*"$/ || desc ~ /^\047.*\047$/) desc=substr(desc, 2, length(desc) - 2)
check_desc()
}
END {
@@ -96,7 +99,7 @@ for f in "${files[@]}"; do
codex_skill="$CODEX_SKILLS_DIR/${skill_dir##*/}"
# Only a relative target resolves in every clone.
if [ "$(readlink "$codex_skill")" != "../../$skill_dir" ]; then
- echo " ❌ $codex_skill: must be a symlink to ../../$skill_dir (ln -s ../../$skill_dir $codex_skill)"
+ echo " ❌ $codex_skill: must be a symlink to ../../$skill_dir (ln -sfn ../../$skill_dir $codex_skill)"
fail=1
fi
@@ -116,11 +119,11 @@ for f in "${files[@]}"; do
case "$claude_flag/$codex_flag" in
true/false | / | /true | false/ | false/true) ;;
true/ | true/true | /false | false/false)
- echo " ❌ $skill_dir: set disable-model-invocation: true and policy.allow_implicit_invocation: false together, or neither"
+ echo " ❌ $f, $policy: set disable-model-invocation: true and policy.allow_implicit_invocation: false together, or neither"
fail=1
;;
*)
- echo " ❌ $skill_dir: disable-model-invocation and policy.allow_implicit_invocation take a bare true or false"
+ echo " ❌ $f, $policy: disable-model-invocation and policy.allow_implicit_invocation take a bare true or false"
fail=1
;;
esac
(iggy) 01/02: chore(repo): make repository skills available to Codex
Posted by hg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
hubcio pushed a commit to branch chore/shared-agent-skills
in repository https://gitbox.apache.org/repos/asf/iggy.git
commit 7b4378203075fa07fab8e4ab882dadf8a11b00ac
Author: Hubert Gruszecki <h....@gmail.com>
AuthorDate: Wed Sep 30 15:03:44 2026 +0200
chore(repo): make repository skills available to Codex
Repository skills were discoverable only through Claude's skill path.
Link the same source for Codex and share review execution rules,
preserving explicit-only reviews without maintaining two copies.
skills.sh checks only tracked skills, so skills that a user installs
locally do not fail it. CI runs the check as well, because a local
hook cannot keep a broken link out of master.
---
.agents/skills/connector-review | 1 +
.agents/skills/connector-runtime | 1 +
.agents/skills/connector-sdk | 1 +
.agents/skills/connector-sink | 1 +
.agents/skills/connector-source | 1 +
.agents/skills/connector-testing | 1 +
.agents/skills/connector-transform | 1 +
.agents/skills/connectors-overview | 1 +
.agents/skills/team-review | 1 +
.agents/skills/team-review-slim | 1 +
.claude/skills/connector-review/SKILL.md | 35 +++-----
.claude/skills/connector-review/agents/openai.yaml | 19 +++++
.claude/skills/team-review-slim/SKILL.md | 31 +++----
.claude/skills/team-review-slim/agents/openai.yaml | 19 +++++
.claude/skills/team-review/SKILL.md | 29 +++----
.claude/skills/team-review/agents/openai.yaml | 19 +++++
.claude/skills/team-review/references/execution.md | 80 +++++++++++++++++
.github/workflows/_common.yml | 7 +-
.pre-commit-config.yaml | 2 +-
AGENTS.md | 20 ++++-
scripts/ci/skills.sh | 99 +++++++++++++++++-----
21 files changed, 284 insertions(+), 86 deletions(-)
diff --git a/.agents/skills/connector-review b/.agents/skills/connector-review
new file mode 120000
index 000000000..aebaee095
--- /dev/null
+++ b/.agents/skills/connector-review
@@ -0,0 +1 @@
+../../.claude/skills/connector-review
\ No newline at end of file
diff --git a/.agents/skills/connector-runtime b/.agents/skills/connector-runtime
new file mode 120000
index 000000000..29a8c00f4
--- /dev/null
+++ b/.agents/skills/connector-runtime
@@ -0,0 +1 @@
+../../.claude/skills/connector-runtime
\ No newline at end of file
diff --git a/.agents/skills/connector-sdk b/.agents/skills/connector-sdk
new file mode 120000
index 000000000..50a581bba
--- /dev/null
+++ b/.agents/skills/connector-sdk
@@ -0,0 +1 @@
+../../.claude/skills/connector-sdk
\ No newline at end of file
diff --git a/.agents/skills/connector-sink b/.agents/skills/connector-sink
new file mode 120000
index 000000000..91ff328db
--- /dev/null
+++ b/.agents/skills/connector-sink
@@ -0,0 +1 @@
+../../.claude/skills/connector-sink
\ No newline at end of file
diff --git a/.agents/skills/connector-source b/.agents/skills/connector-source
new file mode 120000
index 000000000..44bc9b31d
--- /dev/null
+++ b/.agents/skills/connector-source
@@ -0,0 +1 @@
+../../.claude/skills/connector-source
\ No newline at end of file
diff --git a/.agents/skills/connector-testing b/.agents/skills/connector-testing
new file mode 120000
index 000000000..fe4028b19
--- /dev/null
+++ b/.agents/skills/connector-testing
@@ -0,0 +1 @@
+../../.claude/skills/connector-testing
\ No newline at end of file
diff --git a/.agents/skills/connector-transform b/.agents/skills/connector-transform
new file mode 120000
index 000000000..348a042cd
--- /dev/null
+++ b/.agents/skills/connector-transform
@@ -0,0 +1 @@
+../../.claude/skills/connector-transform
\ No newline at end of file
diff --git a/.agents/skills/connectors-overview b/.agents/skills/connectors-overview
new file mode 120000
index 000000000..6049a5978
--- /dev/null
+++ b/.agents/skills/connectors-overview
@@ -0,0 +1 @@
+../../.claude/skills/connectors-overview
\ No newline at end of file
diff --git a/.agents/skills/team-review b/.agents/skills/team-review
new file mode 120000
index 000000000..60a55f5ea
--- /dev/null
+++ b/.agents/skills/team-review
@@ -0,0 +1 @@
+../../.claude/skills/team-review
\ No newline at end of file
diff --git a/.agents/skills/team-review-slim b/.agents/skills/team-review-slim
new file mode 120000
index 000000000..1173951fa
--- /dev/null
+++ b/.agents/skills/team-review-slim
@@ -0,0 +1 @@
+../../.claude/skills/team-review-slim
\ No newline at end of file
diff --git a/.claude/skills/connector-review/SKILL.md b/.claude/skills/connector-review/SKILL.md
index 41284567b..7a63ed955 100644
--- a/.claude/skills/connector-review/SKILL.md
+++ b/.claude/skills/connector-review/SKILL.md
@@ -3,14 +3,20 @@ name: connector-review
description: |
Adversarial review of a connectors PR, branch, or ref range, with clean-room validation of every finding. Experts
work alone and use the focused connector skills as their source of subsystem rules.
+ Use only when the user explicitly requests connector-review.
argument-hint: "[PR number | branch | ref range]"
disable-model-invocation: true
---
# Apache Iggy Connector Review
-`<TARGET>` = `$ARGUMENTS`: a PR number, a branch, or a ref range. Empty means `origin/master...HEAD`. Scope: anything
-under `core/connectors/` plus connector integration tests under `core/integration/tests/connectors/`.
+`<TARGET>` is the PR number, branch, or ref range supplied after the skill name. With no target, review `HEAD`
+from its merge base with `origin/master`. Scope: anything
+under `core/connectors/` plus connector integration tests under `core/integration/tests/connectors/`. Other changed
+paths, such as harness files and `Cargo.toml`, are in scope where connector code depends on them.
+
+Read [execution instructions](../team-review/references/execution.md) before starting. Use the section for your client to
+translate the tool calls below. Include those instructions in worker prompts. Keep this skill's Charter, roles, rounds, and report.
You = **moderator**. You never open the diff or a source file: you route paths, merge claims, synthesize. Every token
you load rides along every later turn. Reviewers and validators are one-shot agents that deliver by writing a file.
@@ -103,25 +109,9 @@ Nobody chats.
## Step 1: Identify the target (no reading)
-- Classify `<TARGET>`: matches `^#?(pr)?[0-9]+$` case-insensitively -> PR, the digits are `<PR>`. Anything else -> ref
- range or bare branch. Empty -> review `origin/master...HEAD`.
-- `<TOPIC>`: `<TARGET>` lowercased, chars outside `[a-z0-9-]` replaced by `-`, repeats collapsed, trimmed, max 40 chars
- (`PR3123` -> `pr3123`, `origin/master...HEAD` -> `origin-master-head`). Empty -> `date +%s`.
-- `<DIR>` = `<session scratchpad dir from your system prompt>/review-<TOPIC>`. `mkdir -p` it.
-- PR: `gh pr view <PR> --json title,body,headRefOid > <DIR>/pr.json`, `gh pr diff <PR> > <DIR>/diff.patch`,
- `gh pr diff <PR> --name-only > <DIR>/files.txt`. `<SHORTCOMMIT>` = first 8 of `headRefOid`.
-- Empty target: `git diff origin/master...HEAD > <DIR>/diff.patch`, same with `--name-only`, and `<SHORTCOMMIT>` =
- `git rev-parse --short=8 HEAD`.
-- Ref range (`A..B` / `A...B`): `git diff <TARGET> > <DIR>/diff.patch`, same with `--name-only`, and `<SHORTCOMMIT>` =
- `git rev-parse --short=8 <TARGET-end>`.
-- Bare branch `<BRANCH>`: `git diff $(git merge-base origin/master <BRANCH>)..<BRANCH> > <DIR>/diff.patch`, same with
- `--name-only`, and `<SHORTCOMMIT>` = `git rev-parse --short=8 <BRANCH>`. Do not create `pr.json` for these three cases.
-- Guard: `git rev-parse HEAD` must equal the reviewed head (`headRefOid` for a PR, `HEAD` for an empty target,
- `<TARGET-end>` for a range, or `<BRANCH>` for a branch). Experts read the local checkout. If it differs, stop and ask
- the user to check out the reviewed head.
-- `<DESCR>`: 1-3 word `snake_case` summary, `[a-z0-9_]`, <= 24 chars. From the PR title; no PR -> from
- `git log -1 --format=%s`.
-- Report path: `<DIR>/report.md`.
+Prepare the target and artifacts as the execution instructions specify. Use the resolved starting commit and reviewed
+head for every round. Read `files.txt` to select roles, but do not read the diff or source files in the moderator
+context.
Do not `cat` any of the files you just wrote. `wc -l <DIR>/diff.patch` is the only look you take.
@@ -232,6 +222,7 @@ Output in the simple English of the Charter. The Charter binds you too:
Confirmed critical + warning only. Simplifications informational. Reason: one line.
Counts: critical N, warning N, nit N, simplify N (Confirmed + Simplification sections)
+Verification: <actual commands and outcomes, or none ran>.
```
Then write `<DIR>/report.md` with:
@@ -243,4 +234,4 @@ Then write `<DIR>/report.md` with:
5. `## Validation record`: counts of PASS / FIX / REMOVE, sweep additions, contested outcomes.
Last user-facing line: `Findings written: <DIR>/report.md`. Do not clean up. One-shot agents end themselves, and
-`<DIR>` stays in the scratchpad.
+`<DIR>` keeps its artifacts.
diff --git a/.claude/skills/connector-review/agents/openai.yaml b/.claude/skills/connector-review/agents/openai.yaml
new file mode 100644
index 000000000..85086e877
--- /dev/null
+++ b/.claude/skills/connector-review/agents/openai.yaml
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+policy:
+ allow_implicit_invocation: false
diff --git a/.claude/skills/team-review-slim/SKILL.md b/.claude/skills/team-review-slim/SKILL.md
index d2a6ce0bc..5c9a2094a 100644
--- a/.claude/skills/team-review-slim/SKILL.md
+++ b/.claude/skills/team-review-slim/SKILL.md
@@ -1,15 +1,20 @@
---
name: team-review-slim
description: |
- Team-review without the verification chain. Reviews a PR, the current branch against master, or of a ref range.
+ Four-expert review of a PR, branch, or ref range, without independent validation rounds.
+ Use only when the user explicitly requests team-review-slim.
argument-hint: "[PR number | branch | ref range]"
disable-model-invocation: true
---
# Apache Iggy Team Review (slim version)
-`<TARGET>` = `$ARGUMENTS`: a PR number, a branch, or a ref range. Empty means `origin/master..HEAD`. Mission critical
-code.
+`<TARGET>` is the PR number, branch, or ref range supplied after the skill name. With no target, review `HEAD`
+from its merge base with `origin/master`. Mission critical code.
+
+Read [execution instructions](../team-review/references/execution.md) before starting. Use the section for your client to
+translate the tool calls below. Include those instructions in worker prompts. Keep this skill's Charter, roles, and report.
+Do not add the full review's validation rounds.
You = **moderator**. You never open the diff or a source file: you route paths, merge claims, synthesize. Every token
you load rides along every later turn. Reviewers are one-shot agents that deliver by writing a file, they do not chat.
@@ -121,20 +126,8 @@ you load rides along every later turn. Reviewers are one-shot agents that delive
## Step 1: Identify the target (no reading)
-- Classify `<TARGET>`: matches `^#?(pr)?[0-9]+$` case-insensitively -> PR, the digits are `<PR>`. Anything else -> ref
- range or bare branch. Empty -> review `origin/master..HEAD`.
-- `<TOPIC>`: `<TARGET>` lowercased, chars outside `[a-z0-9-]` replaced by `-`, repeats collapsed, trimmed, max 40 chars
- (`PR3123` -> `pr3123`, `origin/master..HEAD` -> `origin-master-head`). Empty -> `date +%s`.
-- `<DIR>` = `<session scratchpad dir from your system prompt>/review-<TOPIC>`. `mkdir -p` it.
-- PR: `gh pr view <PR> --json title,body,headRefOid > <DIR>/pr.json`, `gh pr diff <PR> > <DIR>/diff.patch`,
- `gh pr diff <PR> --name-only > <DIR>/files.txt`. `<SHORTCOMMIT>` = first 8 of `headRefOid`.
-- Ref range or bare branch: `git diff $(git merge-base origin/master HEAD)..HEAD > <DIR>/diff.patch`, same with
- `--name-only`, `<SHORTCOMMIT>` = `git rev-parse --short=8 HEAD`. No `pr.json` on this path.
-- Guard: `git rev-parse HEAD` must equal the reviewed head. Experts read the local checkout; if it differs, stop and ask
- the user to check out the reviewed head.
-- `<DESCR>`: 1-3 word `snake_case` summary, `[a-z0-9_]`, \<= 24 chars. From the PR title; no PR -> from
- `git log -1 --format=%s`.
-- Report path: `<DIR>/report.md`.
+Prepare the target and artifacts as the execution instructions specify. Use the resolved starting commit and reviewed
+head for every role. Do not read the diff or source files in the moderator context.
Do not `cat` any of the files you just wrote. `wc -l <DIR>/diff.patch` is the only look you take.
@@ -215,7 +208,7 @@ Reason: one sentence. Only `critical` and `warning` entries with conf:H or conf:
Simplifications are informational.
Counts: critical N, warning N, nit N, simplify N
-Verification: none ran. Open each cited line and confirm the finding before you change the code.
+Verification: <actual commands and outcomes, or none ran>. Open each cited line and confirm the finding before you change the code.
```
Then write `<DIR>/report.md` with:
@@ -227,4 +220,4 @@ Then write `<DIR>/report.md` with:
4. Appendix `## Raw findings per expert`: each role file verbatim, in a fenced block.
Last user-facing line: `Findings written: <DIR>/report.md`. Then name the highest-severity entry in one sentence, and
-stop. There is no cleanup: the one-shot agents end themselves, and `<DIR>` stays in the scratchpad.
+stop. There is no cleanup: the one-shot agents end themselves, and `<DIR>` keeps its artifacts.
diff --git a/.claude/skills/team-review-slim/agents/openai.yaml b/.claude/skills/team-review-slim/agents/openai.yaml
new file mode 100644
index 000000000..85086e877
--- /dev/null
+++ b/.claude/skills/team-review-slim/agents/openai.yaml
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+policy:
+ allow_implicit_invocation: false
diff --git a/.claude/skills/team-review/SKILL.md b/.claude/skills/team-review/SKILL.md
index 4d4605ace..55faac986 100644
--- a/.claude/skills/team-review/SKILL.md
+++ b/.claude/skills/team-review/SKILL.md
@@ -3,14 +3,18 @@ name: team-review
description: |
Adversarial 4-expert review (storage, perf, distsys, ecosystem) of a PR, branch, or ref range, with clean-room
validation of every finding. Experts work alone, no peer debate. Expensive, one run spawns ~10 subagents.
+ Use only when the user explicitly requests team-review.
argument-hint: "[PR number | branch | ref range]"
disable-model-invocation: true
---
# Apache Iggy Team Review
-`<TARGET>` = `$ARGUMENTS`: a PR number, a branch, or a ref range. Empty means `origin/master..HEAD`. Mission critical
-code.
+`<TARGET>` is the PR number, branch, or ref range supplied after the skill name. With no target, review `HEAD`
+from its merge base with `origin/master`. Mission critical code.
+
+Read [execution instructions](references/execution.md) before starting. Use the section for your client to translate
+the tool calls below. Include those instructions in worker prompts. Keep this skill's Charter, roles, rounds, and report.
You = **moderator**. You never open the diff or a source file: you route paths, merge claims, synthesize. Every token
you load rides along every later turn. Reviewers and validators are one-shot agents that deliver by writing a file;
@@ -103,20 +107,8 @@ nobody chats.
## Step 1: Identify the target (no reading)
-- Classify `<TARGET>`: matches `^#?(pr)?[0-9]+$` case-insensitively -> PR, the digits are `<PR>`. Anything else -> ref
- range or bare branch. Empty -> review `origin/master..HEAD`.
-- `<TOPIC>`: `<TARGET>` lowercased, chars outside `[a-z0-9-]` replaced by `-`, repeats collapsed, trimmed, max 40 chars
- (`PR3123` -> `pr3123`, `origin/master..HEAD` -> `origin-master-head`). Empty -> `date +%s`.
-- `<DIR>` = `<session scratchpad dir from your system prompt>/review-<TOPIC>`. `mkdir -p` it.
-- PR: `gh pr view <PR> --json title,body,headRefOid > <DIR>/pr.json`, `gh pr diff <PR> > <DIR>/diff.patch`,
- `gh pr diff <PR> --name-only > <DIR>/files.txt`. `<SHORTCOMMIT>` = first 8 of `headRefOid`.
-- Ref range or bare branch: `git diff $(git merge-base origin/master HEAD)..HEAD > <DIR>/diff.patch`, same with
- `--name-only`, `<SHORTCOMMIT>` = `git rev-parse --short=8 HEAD`. No `pr.json` on this path.
-- Guard: `git rev-parse HEAD` must equal the reviewed head. Experts read the local checkout; if it differs, stop and ask
- the user to check out the reviewed head.
-- `<DESCR>`: 1-3 word `snake_case` summary, `[a-z0-9_]`, \<= 24 chars. From the PR title; no PR -> from
- `git log -1 --format=%s`.
-- Report path: `<DIR>/report.md`.
+Prepare the target and artifacts as the execution instructions specify. Use the resolved starting commit and reviewed
+head for every round. Do not read the diff or source files in the moderator context.
Do not `cat` any of the files you just wrote. `wc -l <DIR>/diff.patch` is the only look you take.
@@ -219,6 +211,7 @@ Output in the simple English of the Charter. The Charter binds you too:
Confirmed critical + warning only. Simplifications informational. Reason: one line.
Counts: critical N, warning N, nit N, simplify N (Confirmed + Simplification sections)
+Verification: <actual commands and outcomes, or none ran>.
```
Then write `<DIR>/report.md` with:
@@ -229,5 +222,5 @@ Then write `<DIR>/report.md` with:
4. Appendix `## Raw findings per expert`: each role file verbatim in a fenced block.
5. `## Validation record`: counts of PASS / FIX / REMOVE, sweep additions, contested outcomes.
-Last user-facing line: `Findings written: <DIR>/report.md`. No cleanup: one-shot agents end themselves, `<DIR>` stays in
-the scratchpad.
+Last user-facing line: `Findings written: <DIR>/report.md`. No cleanup: one-shot agents end themselves, `<DIR>` keeps
+its artifacts.
diff --git a/.claude/skills/team-review/agents/openai.yaml b/.claude/skills/team-review/agents/openai.yaml
new file mode 100644
index 000000000..85086e877
--- /dev/null
+++ b/.claude/skills/team-review/agents/openai.yaml
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+policy:
+ allow_implicit_invocation: false
diff --git a/.claude/skills/team-review/references/execution.md b/.claude/skills/team-review/references/execution.md
new file mode 100644
index 000000000..2505162a8
--- /dev/null
+++ b/.claude/skills/team-review/references/execution.md
@@ -0,0 +1,80 @@
+# Review execution in Claude Code and Codex
+
+These instructions supply target preparation and client-specific tools for `team-review`, `team-review-slim`, and
+`connector-review`. They replace the tool examples in those skills, without changing their roles, Charters, or rounds.
+An explicit skill invocation authorizes its reviewers and validators. Workers do not delegate further.
+
+## Prepare the target
+
+Resolve the repository root with `git rev-parse --show-toplevel`. Run commands there without `git -C`.
+`<TARGET>` is the invocation argument. Claude Code supplies `$ARGUMENTS`; Codex supplies the trailing user text.
+Resolve all refs to full commit IDs before writing artifacts:
+
+- PR number, `#number`, or `prnumber`, case-insensitive: query `gh pr view` for `title`, `body`, `baseRefName`,
+ `baseRefOid`, and `headRefOid`. The reviewed head is `headRefOid`; the start is its merge base with `baseRefOid`.
+- No target: the reviewed head is `HEAD`; the start is its merge base with `origin/master`.
+- Bare branch: the reviewed head is that branch; the start is its merge base with `origin/master`.
+- `A..B`: the start is `A`; the reviewed head is `B`.
+- `A...B`: the reviewed head is `B`; the start is its merge base with `A`.
+
+Fetch missing refs if needed. If resolution fails, report the unresolved target. Never substitute `HEAD` for a named
+branch or use the local `master` tip as the PR base.
+
+Require that the checkout's `HEAD` equals the reviewed head and that tracked files are clean. If either check fails,
+ask the user to prepare the reviewed checkout. Do not reset or stash their changes. Recheck both conditions before
+writing the final report. If the recheck fails, report an incomplete review and give no verdict. Workers read deleted
+files from the resolved starting commit and other files from the checkout.
+
+Derive `<TOPIC>` from the supplied target, or `head-<SHORTCOMMIT>` when absent: lowercase, replace characters outside
+`[a-z0-9-]` with `-`, collapse repeated hyphens, trim them, and keep at most 40 characters. If empty, use `date +%s`.
+Create `<DIR>` with `mktemp -d /tmp/iggy-review-<TOPIC>.XXXXXX`. Retain its artifacts.
+
+- For PRs, capture the complete metadata as `<DIR>/pr.json`. Omit it for other targets.
+- Capture `diff.patch` and `files.txt` from the same resolved start and head. Use `git diff --no-ext-diff --no-color`
+ and its `--name-only` variant. Preserve exact output, including complete hunks and paths.
+- For `connector-review`, if `files.txt` lists no path under `core/connectors/` or `core/integration/tests/connectors/`,
+ report that the target contains no connector changes and stop without a review verdict.
+- `<SHORTCOMMIT>` is the first eight characters of the reviewed head. The report path is `<DIR>/report.md`.
+
+Follow local RTK instructions for ordinary commands. If RTK is in use, use `rtk proxy git diff` and
+`rtk proxy gh pr view` for artifact capture because filtered patches and JSON are unsuitable for workers.
+Give every worker the resolved start and head, repository path, artifact paths, applicable repository instructions,
+its role and Charter, and the execution instructions for its client.
+
+## Claude Code tools
+
+Use the tool calls in each skill as written.
+
+## Codex tools
+
+- Use the available subagent tools and their actual schemas. With `collaboration`, use `spawn_agent`,
+ `wait_agent`, `send_message`, and `followup_task`. Route messages using returned agent IDs.
+- Start independent workers with fresh context, using `fork_turns: "none"` when supported.
+- Inherit the session model. Omit Claude-only fields such as `subagent_type`, `name`, and `model: opus`.
+- Use shell tools for reads and commands, and `apply_patch` for authored files. Read connector guidance from each
+ skill's `SKILL.md`; a Claude `Skill` tool is not required. Resolve relative links against the skill directory.
+- Wait with the available completion tools. Nudge a running worker with `send_message`; resume a completed worker
+ with `followup_task` if its deliverable is missing. Do not request full worker transcripts.
+
+## Clients without a shell
+
+A client without a shell, such as the `/skill` CI runner, cannot run the commands or the checkout checks in this file.
+Skip them. Use the diff and the file list from the client prompt as `diff.patch` and `files.txt`, and create `<DIR>`
+where that prompt puts scratch files.
+
+## Worker lifecycle and reports
+
+Respect the client's concurrency limit, including the moderator's slot. Run roles and validation shards in batches
+when necessary. Run every required role and validation round independently, even when they cannot run together.
+Finish each round before the next consumes its output. If delegation is unavailable, report the limitation.
+Do not present a solo review as the completed team workflow.
+
+Each worker writes its required artifact before returning. Check every expected file after completion. Give a missing
+deliverable one follow-up and, if still absent, one replacement worker. If it remains missing, report the missing role
+or validation shard and an incomplete review. Do not issue a completed verdict.
+
+Workers list each test or build command that they need, with its reason, under `Checks to run:` at the end of their
+deliverable. The moderator runs those checks serially after the workers finish. Record actual commands and outcomes
+in the report's `Verification:` line. For `team-review-slim`, `validation: none (manual)` means no independent
+validation rounds; it does not mean that no tests ran.
+Keep reports local unless the user also requests publication.
diff --git a/.github/workflows/_common.yml b/.github/workflows/_common.yml
index aff8aed46..244cdf9fc 100644
--- a/.github/workflows/_common.yml
+++ b/.github/workflows/_common.yml
@@ -124,6 +124,9 @@ jobs:
- name: Run markdownlint
run: ./scripts/ci/markdownlint.sh --check
+ - name: Check agent skills
+ run: ./scripts/ci/skills.sh
+
shellcheck:
name: Shell scripts lint
runs-on: ubuntu-latest
@@ -309,9 +312,9 @@ jobs:
fi
if [ "$MARKDOWN" = "success" ]; then
- echo "| ✅ Markdown Lint | success | All markdown files are valid |" >> "$GITHUB_STEP_SUMMARY"
+ echo "| ✅ Markdown Lint | success | All markdown files and agent skills are valid |" >> "$GITHUB_STEP_SUMMARY"
elif [ "$MARKDOWN" = "failure" ]; then
- echo "| ❌ Markdown Lint | failure | Markdown formatting issues found |" >> "$GITHUB_STEP_SUMMARY"
+ echo "| ❌ Markdown Lint | failure | Markdown or agent skill issues found |" >> "$GITHUB_STEP_SUMMARY"
else
echo "| ⏭️ Markdown Lint | $MARKDOWN | Check skipped |" >> "$GITHUB_STEP_SUMMARY"
fi
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index cfd967de8..92da570fb 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -162,7 +162,7 @@ repos:
name: skills (AI agent skill frontmatter + link check)
entry: ./scripts/ci/skills.sh
language: system
- files: ^\.claude/skills/.*\.md$
+ always_run: true
pass_filenames: false
- id: taplo
diff --git a/AGENTS.md b/AGENTS.md
index 7d68104aa..bd20c1d7d 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -6,7 +6,8 @@ Transports: QUIC, WebSocket, TCP (custom binary), HTTP (REST). SDKs:
Rust, .NET, Java, Python, Go, C++, Node.js. A connectors subsystem
ingests from / egresses to external systems via dlopened plugins.
-> Skills live under `.claude/skills/`. Load
+> Skills live under `.claude/skills/`, with per-skill links under
+> `.agents/skills/` for Codex. Load
> [connectors-overview](.claude/skills/connectors-overview/SKILL.md)
> first for any change under `core/connectors/`. Other subsystems
> follow the repo-wide principles in this file.
@@ -134,6 +135,9 @@ iggy/
Each `SKILL.md` has YAML frontmatter (name, description). For
connectors work, load `connectors-overview` first as router.
+Claude Code uses `/skill-name`; Codex uses `$skill-name` or `/skills`.
+Keep one source under `.claude/skills/` and link its directory from
+`.agents/skills/`. Keep templates and references inside the source directory.
- [connectors-overview](.claude/skills/connectors-overview/SKILL.md) - router + universal connector rules
- [connector-runtime](.claude/skills/connector-runtime/SKILL.md) - FFI host, lifecycle, state, metrics
@@ -143,12 +147,20 @@ connectors work, load `connectors-overview` first as router.
- [connector-transform](.claude/skills/connector-transform/SKILL.md) - transform authoring
- [connector-testing](.claude/skills/connector-testing/SKILL.md) - unit + integration test patterns
-Repo-wide, user-invoked only. `disable-model-invocation: true` keeps it
-out of the agent's context; do not replicate its steps. When a
-non-trivial change passes verification, suggest `/team-review <target>`.
+Review skills are user-invoked only. Keep `disable-model-invocation: true`
+for Claude Code and `policy.allow_implicit_invocation: false` in
+`agents/openai.yaml` for Codex. Do not replicate their steps without an
+explicit invocation. When a non-trivial change passes verification,
+suggest `/team-review <target>` in Claude Code or `$team-review <target>`
+in Codex.
- [connector-review](.claude/skills/connector-review/SKILL.md) - adversarial connectors PR/branch review with validation
- [team-review](.claude/skills/team-review/SKILL.md) - adversarial 4-expert PR/branch review, ~10 subagents per run
+- [team-review-slim](.claude/skills/team-review-slim/SKILL.md) - four-expert review without independent validation rounds
+
+Review skills share [execution instructions](.claude/skills/team-review/references/execution.md)
+for target preparation and client-specific agent tools. Run
+`./scripts/ci/skills.sh` to check skill metadata, links, and invocation policies.
## Repo-wide principles
diff --git a/scripts/ci/skills.sh b/scripts/ci/skills.sh
index 6399d15ec..e7e0c94f6 100755
--- a/scripts/ci/skills.sh
+++ b/scripts/ci/skills.sh
@@ -16,10 +16,13 @@
# specific language governing permissions and limitations
# under the License.
-# Validate AI agent skills under .claude/skills/:
+# Validate the AI agent skills that git tracks:
# - YAML frontmatter present, non-empty `name` (kebab-case, <= 64 chars)
-# - non-empty `description` (<= 1024 chars)
-# - relative markdown links inside SKILL.md / TEMPLATE.md resolve
+# - non-empty `description` (<= 1024 chars, block scalars included)
+# - relative markdown links inside skills, templates, and references resolve
+# - every skill has a relative Codex discovery link, and every link has a skill
+# - Claude Code and Codex agree on whether a skill is user-only
+# Untracked skills that a user installs locally are not checked.
set -euo pipefail
@@ -30,15 +33,11 @@ ROOT="$(git rev-parse --show-toplevel)"
cd "$ROOT"
SKILLS_DIR=".claude/skills"
+CODEX_SKILLS_DIR=".agents/skills"
fail=0
-if [ ! -d "$SKILLS_DIR" ]; then
- echo "ℹ️ No $SKILLS_DIR directory; nothing to validate"
- exit 0
-fi
-
-shopt -s nullglob
-files=("$SKILLS_DIR"/*/SKILL.md "$SKILLS_DIR"/*/TEMPLATE.md)
+mapfile -t files < <(git ls-files -- ":(glob)$SKILLS_DIR/*/SKILL.md" \
+ ":(glob)$SKILLS_DIR/*/TEMPLATE.md" ":(glob)$SKILLS_DIR/*/references/*.md")
if [ ${#files[@]} -eq 0 ]; then
echo "ℹ️ No skill files found under $SKILLS_DIR"
exit 0
@@ -47,33 +46,93 @@ fi
echo "🔍 Validating ${#files[@]} skill file(s)..."
# Frontmatter check (SKILL.md only).
-for f in "$SKILLS_DIR"/*/SKILL.md; do
+for f in "${files[@]}"; do
+ [[ "$f" == */SKILL.md ]] || continue
awk '
+ # END runs after every exit, so it must not report a second error.
+ function die(msg) { print " ❌ '"$f"': " msg; failed=1; exit 1 }
+ function check_desc() {
+ if (desc == "") die("empty description")
+ if (length(desc) > 1024) die("description > 1024 chars")
+ }
BEGIN { state=0 }
- NR==1 && $0!="---" { print " ❌ '"$f"': missing YAML frontmatter on line 1"; exit 1 }
+ NR==1 && $0!="---" { die("missing YAML frontmatter on line 1") }
NR==1 { state=1; next }
+ # A block scalar (description: |) continues on the indented and blank lines below it.
+ state==1 && in_desc && (/^[[:space:]]/ || $0 == "") {
+ line=$0; sub(/^[[:space:]]+/, "", line)
+ desc = desc (desc == "" ? "" : " ") line
+ next
+ }
+ state==1 && in_desc { in_desc=0; check_desc() }
state==1 && $0=="---" { state=2; exit 0 }
state==1 && /^name:[[:space:]]/ {
name=$0; sub(/^name:[[:space:]]*/, "", name);
- if (name == "") { print " ❌ '"$f"': empty name"; exit 1 }
- if (length(name) > 64) { print " ❌ '"$f"': name > 64 chars"; exit 1 }
- if (name !~ /^[a-z0-9][a-z0-9-]*$/) { print " ❌ '"$f"': name not kebab-case: " name; exit 1 }
+ if (name == "") die("empty name")
+ if (length(name) > 64) die("name > 64 chars")
+ if (name !~ /^[a-z0-9][a-z0-9-]*$/) die("name not kebab-case: " name)
has_name=1
}
state==1 && /^description:[[:space:]]/ {
desc=$0; sub(/^description:[[:space:]]*/, "", desc);
- if (desc == "") { print " ❌ '"$f"': empty description"; exit 1 }
- if (length(desc) > 1024) { print " ❌ '"$f"': description > 1024 chars"; exit 1 }
has_desc=1
+ if (desc ~ /^[|>][-+]?$/) { desc=""; in_desc=1; next }
+ check_desc()
}
END {
- if (state != 2) { print " ❌ '"$f"': frontmatter not closed"; exit 1 }
- if (!has_name) { print " ❌ '"$f"': missing name field"; exit 1 }
- if (!has_desc) { print " ❌ '"$f"': missing description field"; exit 1 }
+ if (failed) exit 1
+ if (state != 2) die("frontmatter not closed")
+ if (!has_name) die("missing name field")
+ if (!has_desc) die("missing description field")
}
' "$f" || fail=1
done
+# Codex finds skills through links under .agents/skills, and both clients must
+# agree on whether a skill is user-only.
+for f in "${files[@]}"; do
+ [[ "$f" == */SKILL.md ]] || continue
+ skill_dir=${f%/SKILL.md}
+ codex_skill="$CODEX_SKILLS_DIR/${skill_dir##*/}"
+ # Only a relative target resolves in every clone.
+ if [ "$(readlink "$codex_skill")" != "../../$skill_dir" ]; then
+ echo " ❌ $codex_skill: must be a symlink to ../../$skill_dir (ln -s ../../$skill_dir $codex_skill)"
+ fail=1
+ fi
+
+ claude_flag=$(awk '
+ /^---$/ { if (++section == 2) exit; next }
+ section == 1 && sub(/^disable-model-invocation:[[:space:]]*/, "") { sub(/[[:space:]]+$/, ""); print; exit }
+ ' "$f")
+ codex_flag=""
+ policy="$skill_dir/agents/openai.yaml"
+ if [ -f "$policy" ]; then
+ codex_flag=$(awk '
+ /^policy:/ { in_policy=1; next }
+ /^[^[:space:]#]/ { in_policy=0 }
+ in_policy && sub(/^[[:space:]]+allow_implicit_invocation:[[:space:]]*/, "") { sub(/[[:space:]]+$/, ""); print; exit }
+ ' "$policy")
+ fi
+ case "$claude_flag/$codex_flag" in
+ true/false | / | /true | false/ | false/true) ;;
+ true/ | true/true | /false | false/false)
+ echo " ❌ $skill_dir: set disable-model-invocation: true and policy.allow_implicit_invocation: false together, or neither"
+ fail=1
+ ;;
+ *)
+ echo " ❌ $skill_dir: disable-model-invocation and policy.allow_implicit_invocation take a bare true or false"
+ fail=1
+ ;;
+ esac
+done
+
+while IFS= read -r codex_skill; do
+ if [ ! -f "$SKILLS_DIR/${codex_skill#"$CODEX_SKILLS_DIR"/}/SKILL.md" ]; then
+ echo " ❌ $codex_skill: no corresponding skill under $SKILLS_DIR"
+ fail=1
+ fi
+done < <(git ls-files -- "$CODEX_SKILLS_DIR")
+
# Relative link resolution.
for f in "${files[@]}"; do
dir=$(dirname "$f")