From 8fbc309833e4c9222dfb949b0a8e409e1f243186 Mon Sep 17 00:00:00 2001 From: Lars Moelleken Date: Mon, 17 Aug 2026 07:55:36 +0200 Subject: [PATCH 01/10] ci: make agent-loop the release-set root authority --- tools/agent-loop/verify-release-set.php | 160 ++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 tools/agent-loop/verify-release-set.php diff --git a/tools/agent-loop/verify-release-set.php b/tools/agent-loop/verify-release-set.php new file mode 100644 index 0000000..ab4145d --- /dev/null +++ b/tools/agent-loop/verify-release-set.php @@ -0,0 +1,160 @@ + 'voku/agent-kanban', + 'agent_learning_release' => 'voku/agent-learning', + 'agent_map_release' => 'voku/agent-map', + 'agent_recall_compiler_release' => 'voku/agent-recall-compiler', + 'agent_session_release' => 'voku/agent-session', +]; + +if ($argc < 3 || $argc > 4) { + fwrite(STDERR, "Usage: php tools/agent-loop/verify-release-set.php [composer.lock]\n"); + exit(2); +} + +try { + $issue = readJsonObject($argv[1]); + $composer = readJsonObject($argv[2]); + $toolchain = requireArray($issue, 'toolchain', $argv[1]); + $require = stringRequirements($composer['require'] ?? [], 'require', $argv[2]); + $requireDev = stringRequirements($composer['require-dev'] ?? [], 'require-dev', $argv[2]); + $rootRequirements = $require + $requireDev; + + $expectedAgentLoop = requireString($toolchain, 'agent_loop_release', $argv[1]); + $actualAgentLoop = $rootRequirements[AGENT_LOOP_PACKAGE] ?? null; + if ($actualAgentLoop !== $expectedAgentLoop) { + throw new RuntimeException(sprintf( + '%s must require %s %s; got %s.', + $argv[2], + AGENT_LOOP_PACKAGE, + $expectedAgentLoop, + $actualAgentLoop ?? '', + )); + } + + foreach (OWNED_AGENT_PACKAGES as $package) { + if (isset($rootRequirements[$package])) { + throw new RuntimeException(sprintf( + '%s must not constrain %s directly; %s owns the first-party release set.', + $argv[2], + $package, + AGENT_LOOP_PACKAGE, + )); + } + } + + if ($argc === 4) { + $lock = readJsonObject($argv[3]); + $resolved = resolvedVersions($lock, $argv[3]); + foreach (OWNED_AGENT_PACKAGES as $field => $package) { + $expected = requireString($toolchain, $field, $argv[1]); + $actual = $resolved[$package] ?? null; + if ($actual !== $expected) { + throw new RuntimeException(sprintf( + 'Resolved %s must be %s; got %s.', + $package, + $expected, + $actual ?? '', + )); + } + } + } +} catch (Throwable $exception) { + fwrite(STDERR, $exception->getMessage() . "\n"); + exit(1); +} + +/** @return array */ +function readJsonObject(string $path): array +{ + $content = file_get_contents($path); + if ($content === false) { + throw new RuntimeException('Cannot read ' . $path . '.'); + } + + $decoded = json_decode($content, true, 512, JSON_THROW_ON_ERROR); + if (!is_array($decoded)) { + throw new RuntimeException($path . ' must contain a JSON object.'); + } + + return $decoded; +} + +/** + * @param array $data + * @return array + */ +function requireArray(array $data, string $key, string $path): array +{ + $value = $data[$key] ?? null; + if (!is_array($value)) { + throw new RuntimeException(sprintf('%s.%s must be an object.', $path, $key)); + } + + return $value; +} + +/** @param array $data */ +function requireString(array $data, string $key, string $path): string +{ + $value = $data[$key] ?? null; + if (!is_string($value) || $value === '') { + throw new RuntimeException(sprintf('%s.%s must be a non-empty string.', $path, $key)); + } + + return $value; +} + +/** + * @param mixed $requirements + * @return array + */ +function stringRequirements(mixed $requirements, string $section, string $path): array +{ + if (!is_array($requirements)) { + throw new RuntimeException(sprintf('%s.%s must be an object.', $path, $section)); + } + + $result = []; + foreach ($requirements as $package => $constraint) { + if (!is_string($package) || !is_string($constraint)) { + throw new RuntimeException(sprintf('%s.%s must contain string package constraints.', $path, $section)); + } + $result[$package] = $constraint; + } + + return $result; +} + +/** + * @param array $lock + * @return array + */ +function resolvedVersions(array $lock, string $path): array +{ + $resolved = []; + foreach (['packages', 'packages-dev'] as $section) { + $packages = $lock[$section] ?? []; + if (!is_array($packages)) { + throw new RuntimeException(sprintf('%s.%s must be an array.', $path, $section)); + } + foreach ($packages as $package) { + if (!is_array($package)) { + continue; + } + $name = $package['name'] ?? null; + $version = $package['version'] ?? null; + if (is_string($name) && is_string($version)) { + $resolved[$name] = $version; + } + } + } + + return $resolved; +} From a2250b396b2a393aeeaa22e5278031d0fe26e6da Mon Sep 17 00:00:00 2001 From: Lars Moelleken Date: Mon, 17 Aug 2026 07:55:44 +0200 Subject: [PATCH 02/10] ci: let agent-loop own sibling dependency versions --- tools/agent-loop/composer.json | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/tools/agent-loop/composer.json b/tools/agent-loop/composer.json index 2cb05c2..fb89d70 100644 --- a/tools/agent-loop/composer.json +++ b/tools/agent-loop/composer.json @@ -4,15 +4,8 @@ "type": "project", "license": "Apache-2.0", "require-dev": { - "voku/agent-kanban": "0.3.1", - "voku/agent-learning": "0.13.0", "voku/agent-loop": "0.16.5", - "voku/agent-map": "0.8.1", - "voku/agent-recall-compiler": "0.13.2", - "voku/agent-session": "0.6.0", - "voku/simple-cache": "6.1.0", - "voku/simple-php-code-parser": "0.22.2", - "voku/stop-words": "2.0.1" + "voku/simple-php-code-parser": "0.22.2" }, "minimum-stability": "dev", "prefer-stable": true, From 1f44bba05ef355f4c06c2469fffdbfe041f6196e Mon Sep 17 00:00:00 2001 From: Lars Moelleken Date: Mon, 17 Aug 2026 07:55:59 +0200 Subject: [PATCH 03/10] ci: bind replay evidence to resolved first-party versions --- tools/agent-loop/dogfood/issue-60.json | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/agent-loop/dogfood/issue-60.json b/tools/agent-loop/dogfood/issue-60.json index 6cc2a36..b62fb43 100644 --- a/tools/agent-loop/dogfood/issue-60.json +++ b/tools/agent-loop/dogfood/issue-60.json @@ -17,7 +17,6 @@ "agent_recall_compiler_release": "0.13.2", "agent_session_release": "0.6.0", "agent_skills_commit": "c7e9d8bdda59d957600bca8dc9f787f03286b277", - "resolved_lock_sha256": "sha256:6079c76aec73038e61875292c242477f74ed8913920c3ecfc496bd0ea3157b82", "operating_prompt": "reproduce-before-fix" } } From 3c3b14eb45556d48e86cfd409e78a8368a67dab8 Mon Sep 17 00:00:00 2001 From: Lars Moelleken Date: Mon, 17 Aug 2026 07:56:18 +0200 Subject: [PATCH 04/10] ci: verify resolved agent-loop release set --- .../agent-loop-real-issue-dogfood.yml | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/.github/workflows/agent-loop-real-issue-dogfood.yml b/.github/workflows/agent-loop-real-issue-dogfood.yml index 2e46b82..e831e0d 100644 --- a/.github/workflows/agent-loop-real-issue-dogfood.yml +++ b/.github/workflows/agent-loop-real-issue-dogfood.yml @@ -49,29 +49,23 @@ jobs: coverage: none tools: composer:v2 - - name: Validate frozen input and released toolchain + - name: Validate frozen input and release-set authority run: | set -euo pipefail issue='harness/tools/agent-loop/dogfood/issue-60.json' composer='harness/tools/agent-loop/composer.json' test "$(jq -r '.target_base_commit' "${issue}")" = "$(git -C target rev-parse HEAD)" test "$(jq -r '.toolchain.agent_skills_commit' "${issue}")" = "$(git -C skills rev-parse HEAD)" - test "$(jq -r '.toolchain.agent_loop_release' "${issue}")" = "$(jq -r '.["require-dev"]["voku/agent-loop"]' "${composer}")" - test "$(jq -r '.toolchain.agent_kanban_release' "${issue}")" = "$(jq -r '.["require-dev"]["voku/agent-kanban"]' "${composer}")" - test "$(jq -r '.toolchain.agent_learning_release' "${issue}")" = "$(jq -r '.["require-dev"]["voku/agent-learning"]' "${composer}")" - test "$(jq -r '.toolchain.agent_map_release' "${issue}")" = "$(jq -r '.["require-dev"]["voku/agent-map"]' "${composer}")" - test "$(jq -r '.toolchain.agent_recall_compiler_release' "${issue}")" = "$(jq -r '.["require-dev"]["voku/agent-recall-compiler"]' "${composer}")" - test "$(jq -r '.toolchain.agent_session_release' "${issue}")" = "$(jq -r '.["require-dev"]["voku/agent-session"]' "${composer}")" - - - name: Resolve and verify exact released agent toolchain + php harness/tools/agent-loop/verify-release-set.php "${issue}" "${composer}" + + - name: Resolve and verify released agent toolchain run: | set -euo pipefail issue='harness/tools/agent-loop/dogfood/issue-60.json' + composer='harness/tools/agent-loop/composer.json' lock='harness/tools/agent-loop/composer.lock' composer update --working-dir=harness/tools/agent-loop --no-interaction --prefer-dist --no-progress - expected="$(jq -r '.toolchain.resolved_lock_sha256' "${issue}")" - actual="sha256:$(sha256sum "${lock}" | cut -d ' ' -f 1)" - test "${actual}" = "${expected}" + php harness/tools/agent-loop/verify-release-set.php "${issue}" "${composer}" "${lock}" - name: Install historical target dependencies run: composer install --working-dir=target --no-interaction --prefer-dist --no-progress From bbc3df438c5bd82bcc2739b7b50d31c5a63bdb92 Mon Sep 17 00:00:00 2001 From: Lars Moelleken Date: Mon, 17 Aug 2026 07:56:41 +0200 Subject: [PATCH 05/10] ci: reuse release-set authority check for issue 101 --- .../workflows/agent-loop-issue-101-dogfood.yml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/.github/workflows/agent-loop-issue-101-dogfood.yml b/.github/workflows/agent-loop-issue-101-dogfood.yml index d0f8b6a..c4ce21b 100644 --- a/.github/workflows/agent-loop-issue-101-dogfood.yml +++ b/.github/workflows/agent-loop-issue-101-dogfood.yml @@ -50,24 +50,23 @@ jobs: coverage: none tools: composer:v2 - - name: Validate frozen input and released toolchain + - name: Validate frozen input and release-set authority run: | set -euo pipefail issue='harness/tools/agent-loop/dogfood/issue-101.json' composer='harness/tools/agent-loop/composer.json' test "$(jq -r '.target_base_commit' "${issue}")" = "$(git -C target rev-parse HEAD)" test "$(jq -r '.toolchain.agent_skills_commit' "${issue}")" = "$(git -C skills rev-parse HEAD)" - test "$(jq -r '.toolchain.agent_loop_release' "${issue}")" = "$(jq -r '.["require-dev"]["voku/agent-loop"]' "${composer}")" - test "$(jq -r '.toolchain.agent_kanban_release' "${issue}")" = "$(jq -r '.["require-dev"]["voku/agent-kanban"]' "${composer}")" - test "$(jq -r '.toolchain.agent_learning_release' "${issue}")" = "$(jq -r '.["require-dev"]["voku/agent-learning"]' "${composer}")" - test "$(jq -r '.toolchain.agent_map_release' "${issue}")" = "$(jq -r '.["require-dev"]["voku/agent-map"]' "${composer}")" - test "$(jq -r '.toolchain.agent_recall_compiler_release' "${issue}")" = "$(jq -r '.["require-dev"]["voku/agent-recall-compiler"]' "${composer}")" - test "$(jq -r '.toolchain.agent_session_release' "${issue}")" = "$(jq -r '.["require-dev"]["voku/agent-session"]' "${composer}")" - - - name: Install exact released agent toolchain + php harness/tools/agent-loop/verify-release-set.php "${issue}" "${composer}" + + - name: Install and verify released agent toolchain run: | set -euo pipefail + issue='harness/tools/agent-loop/dogfood/issue-101.json' + composer='harness/tools/agent-loop/composer.json' + lock='harness/tools/agent-loop/composer.lock' composer update --working-dir=harness/tools/agent-loop --no-interaction --prefer-dist --no-progress + php harness/tools/agent-loop/verify-release-set.php "${issue}" "${composer}" "${lock}" composer update --working-dir=target --no-interaction --prefer-dist --no-progress - name: Freeze agent-map context from issue text before any fix From a901a37dd883f152fe6962efafe559244ba0832b Mon Sep 17 00:00:00 2001 From: Lars Moelleken Date: Mon, 17 Aug 2026 07:57:41 +0200 Subject: [PATCH 06/10] docs: separate release constraints from replay evidence --- tools/agent-loop/README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/agent-loop/README.md b/tools/agent-loop/README.md index 455c531..f48dad9 100644 --- a/tools/agent-loop/README.md +++ b/tools/agent-loop/README.md @@ -4,6 +4,8 @@ This directory is a separate Composer project on purpose. `voku/agent-loop` is development tooling for this repository, not part of the package contract of `voku/simple-php-code-parser`. Keeping it out of the root `composer.json` avoids raising consumer PHP requirements, leaking agent tooling into downstream installs, and creating first-party dependency cycles such as `agent-map -> agent-loop -> agent-recall-compiler -> agent-map`. +`voku/agent-loop` is also the single root authority for its `agent-*` runtime dependency set. This tool project constrains `agent-loop` directly and keeps `voku/simple-php-code-parser` direct because that is the package under test. The resolved sibling `agent-*` versions are replay evidence, not duplicate root constraints. + ## Real-issue replay The first replay uses historical issue #60, `Update for use with PHP 8.4`. @@ -11,9 +13,11 @@ The first replay uses historical issue #60, `Update for use with PHP 8.4`. The workflow freezes three things before context discovery: - issue title/body and the pre-fix base commit `5156d5d74ca1bce275219f4571efd54ec44be911`; -- the released agent toolchain: `agent-loop 0.16.5`, `agent-kanban 0.3.1`, `agent-learning 0.13.0`, `agent-map 0.8.1`, `agent-recall-compiler 0.13.2`, and `agent-session 0.6.0`; +- `agent-loop 0.16.5` as the release-set authority plus the expected resolved siblings: `agent-kanban 0.3.1`, `agent-learning 0.13.0`, `agent-map 0.8.1`, `agent-recall-compiler 0.13.2`, and `agent-session 0.6.0`; - agent-skills commit `c7e9d8bdda59d957600bca8dc9f787f03286b277` and the `reproduce-before-fix` L2 recipe. +Before resolution, `verify-release-set.php` fails if the tool project reintroduces direct sibling `agent-*` constraints. After resolution, the same verifier checks the frozen sibling versions against `composer.lock`. + The issue input contains no knowledge of the later fix files. `issue-60-oracle.json` is read only after map search and Recall compilation have finished. The historical fix in PR #84 changed: @@ -27,4 +31,4 @@ A map miss is recorded as a finding rather than converted into a fake correctnes ## Evidence -GitHub Actions archives the generated tool `composer.lock`, resolved package list, map search output, Recall bundle/facts/system prompt, and post-context evaluation. The lock proves the exact released package set Composer resolved for the run; no sibling checkout or candidate path repository participates in normal replay evidence. +GitHub Actions archives the generated tool `composer.lock`, resolved package list, map search output, Recall bundle/facts/system prompt, and post-context evaluation. The lock proves the exact package set Composer resolved for the run; the frozen first-party versions prove that `agent-loop` still resolved the expected coordinated toolchain without a sibling checkout, candidate path repository, or duplicate root pin participating in normal replay evidence. From dda059aecd8cf245a53bd44959a7f1baedecb5d2 Mon Sep 17 00:00:00 2001 From: Lars Moelleken Date: Mon, 17 Aug 2026 07:59:37 +0200 Subject: [PATCH 07/10] ci: record resolved siblings instead of constraining them --- tools/agent-loop/verify-release-set.php | 79 +++++++++++++++++-------- 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/tools/agent-loop/verify-release-set.php b/tools/agent-loop/verify-release-set.php index ab4145d..a5ad658 100644 --- a/tools/agent-loop/verify-release-set.php +++ b/tools/agent-loop/verify-release-set.php @@ -2,15 +2,20 @@ declare(strict_types=1); -use RuntimeException; - const AGENT_LOOP_PACKAGE = 'voku/agent-loop'; const OWNED_AGENT_PACKAGES = [ - 'agent_kanban_release' => 'voku/agent-kanban', - 'agent_learning_release' => 'voku/agent-learning', - 'agent_map_release' => 'voku/agent-map', - 'agent_recall_compiler_release' => 'voku/agent-recall-compiler', - 'agent_session_release' => 'voku/agent-session', + 'voku/agent-kanban', + 'voku/agent-learning', + 'voku/agent-map', + 'voku/agent-recall-compiler', + 'voku/agent-session', +]; +const STALE_SIBLING_RELEASE_FIELDS = [ + 'agent_kanban_release', + 'agent_learning_release', + 'agent_map_release', + 'agent_recall_compiler_release', + 'agent_session_release', ]; if ($argc < 3 || $argc > 4) { @@ -29,7 +34,7 @@ $expectedAgentLoop = requireString($toolchain, 'agent_loop_release', $argv[1]); $actualAgentLoop = $rootRequirements[AGENT_LOOP_PACKAGE] ?? null; if ($actualAgentLoop !== $expectedAgentLoop) { - throw new RuntimeException(sprintf( + throw new \RuntimeException(sprintf( '%s must require %s %s; got %s.', $argv[2], AGENT_LOOP_PACKAGE, @@ -40,7 +45,7 @@ foreach (OWNED_AGENT_PACKAGES as $package) { if (isset($rootRequirements[$package])) { - throw new RuntimeException(sprintf( + throw new \RuntimeException(sprintf( '%s must not constrain %s directly; %s owns the first-party release set.', $argv[2], $package, @@ -49,21 +54,43 @@ } } + foreach (STALE_SIBLING_RELEASE_FIELDS as $field) { + if (array_key_exists($field, $toolchain)) { + throw new \RuntimeException(sprintf( + '%s.toolchain.%s duplicates transitive release-set authority; record the resolved lock instead.', + $argv[1], + $field, + )); + } + } + if ($argc === 4) { $lock = readJsonObject($argv[3]); $resolved = resolvedVersions($lock, $argv[3]); - foreach (OWNED_AGENT_PACKAGES as $field => $package) { - $expected = requireString($toolchain, $field, $argv[1]); - $actual = $resolved[$package] ?? null; - if ($actual !== $expected) { - throw new RuntimeException(sprintf( - 'Resolved %s must be %s; got %s.', - $package, - $expected, - $actual ?? '', - )); + $resolvedAgentLoop = $resolved[AGENT_LOOP_PACKAGE] ?? null; + if ($resolvedAgentLoop !== $expectedAgentLoop) { + throw new \RuntimeException(sprintf( + 'Resolved %s must be %s; got %s.', + AGENT_LOOP_PACKAGE, + $expectedAgentLoop, + $resolvedAgentLoop ?? '', + )); + } + + $releaseSet = [AGENT_LOOP_PACKAGE => $resolvedAgentLoop]; + foreach (OWNED_AGENT_PACKAGES as $package) { + $version = $resolved[$package] ?? null; + if ($version === null) { + throw new \RuntimeException('Resolved release set is missing ' . $package . '.'); } + $releaseSet[$package] = $version; } + ksort($releaseSet, SORT_STRING); + + fwrite(STDOUT, json_encode( + ['resolved_agent_release_set' => $releaseSet], + JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR, + ) . PHP_EOL); } } catch (Throwable $exception) { fwrite(STDERR, $exception->getMessage() . "\n"); @@ -75,12 +102,12 @@ function readJsonObject(string $path): array { $content = file_get_contents($path); if ($content === false) { - throw new RuntimeException('Cannot read ' . $path . '.'); + throw new \RuntimeException('Cannot read ' . $path . '.'); } $decoded = json_decode($content, true, 512, JSON_THROW_ON_ERROR); if (!is_array($decoded)) { - throw new RuntimeException($path . ' must contain a JSON object.'); + throw new \RuntimeException($path . ' must contain a JSON object.'); } return $decoded; @@ -94,7 +121,7 @@ function requireArray(array $data, string $key, string $path): array { $value = $data[$key] ?? null; if (!is_array($value)) { - throw new RuntimeException(sprintf('%s.%s must be an object.', $path, $key)); + throw new \RuntimeException(sprintf('%s.%s must be an object.', $path, $key)); } return $value; @@ -105,7 +132,7 @@ function requireString(array $data, string $key, string $path): string { $value = $data[$key] ?? null; if (!is_string($value) || $value === '') { - throw new RuntimeException(sprintf('%s.%s must be a non-empty string.', $path, $key)); + throw new \RuntimeException(sprintf('%s.%s must be a non-empty string.', $path, $key)); } return $value; @@ -118,13 +145,13 @@ function requireString(array $data, string $key, string $path): string function stringRequirements(mixed $requirements, string $section, string $path): array { if (!is_array($requirements)) { - throw new RuntimeException(sprintf('%s.%s must be an object.', $path, $section)); + throw new \RuntimeException(sprintf('%s.%s must be an object.', $path, $section)); } $result = []; foreach ($requirements as $package => $constraint) { if (!is_string($package) || !is_string($constraint)) { - throw new RuntimeException(sprintf('%s.%s must contain string package constraints.', $path, $section)); + throw new \RuntimeException(sprintf('%s.%s must contain string package constraints.', $path, $section)); } $result[$package] = $constraint; } @@ -142,7 +169,7 @@ function resolvedVersions(array $lock, string $path): array foreach (['packages', 'packages-dev'] as $section) { $packages = $lock[$section] ?? []; if (!is_array($packages)) { - throw new RuntimeException(sprintf('%s.%s must be an array.', $path, $section)); + throw new \RuntimeException(sprintf('%s.%s must be an array.', $path, $section)); } foreach ($packages as $package) { if (!is_array($package)) { From bfafb74c1b9b5629bf966ba409f435b22f6ff176 Mon Sep 17 00:00:00 2001 From: Lars Moelleken Date: Mon, 17 Aug 2026 07:59:53 +0200 Subject: [PATCH 08/10] ci: keep only direct replay toolchain authority --- tools/agent-loop/dogfood/issue-60.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tools/agent-loop/dogfood/issue-60.json b/tools/agent-loop/dogfood/issue-60.json index b62fb43..d89438e 100644 --- a/tools/agent-loop/dogfood/issue-60.json +++ b/tools/agent-loop/dogfood/issue-60.json @@ -11,11 +11,6 @@ "target_base_commit": "5156d5d74ca1bce275219f4571efd54ec44be911", "toolchain": { "agent_loop_release": "0.16.5", - "agent_kanban_release": "0.3.1", - "agent_learning_release": "0.13.0", - "agent_map_release": "0.8.1", - "agent_recall_compiler_release": "0.13.2", - "agent_session_release": "0.6.0", "agent_skills_commit": "c7e9d8bdda59d957600bca8dc9f787f03286b277", "operating_prompt": "reproduce-before-fix" } From 6a242b7eb11a98e6f0b8c11a8f103ccc00960f27 Mon Sep 17 00:00:00 2001 From: Lars Moelleken Date: Mon, 17 Aug 2026 08:00:00 +0200 Subject: [PATCH 09/10] ci: keep only direct issue 101 toolchain authority --- tools/agent-loop/dogfood/issue-101.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tools/agent-loop/dogfood/issue-101.json b/tools/agent-loop/dogfood/issue-101.json index 86b2d43..46f2dd2 100644 --- a/tools/agent-loop/dogfood/issue-101.json +++ b/tools/agent-loop/dogfood/issue-101.json @@ -11,11 +11,6 @@ "target_base_commit": "53f1b5085ee883560afa9326ee914f6b23acd6ae", "toolchain": { "agent_loop_release": "0.16.5", - "agent_kanban_release": "0.3.1", - "agent_learning_release": "0.13.0", - "agent_map_release": "0.8.1", - "agent_recall_compiler_release": "0.13.2", - "agent_session_release": "0.6.0", "agent_skills_commit": "c7e9d8bdda59d957600bca8dc9f787f03286b277", "operating_prompt": "reproduce-before-fix" } From 540aad74aaa6d47e7476872088e9d7d8c640d795 Mon Sep 17 00:00:00 2001 From: Lars Moelleken Date: Mon, 17 Aug 2026 08:00:14 +0200 Subject: [PATCH 10/10] docs: record resolved release set as evidence --- tools/agent-loop/README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tools/agent-loop/README.md b/tools/agent-loop/README.md index f48dad9..fcea1b0 100644 --- a/tools/agent-loop/README.md +++ b/tools/agent-loop/README.md @@ -4,7 +4,7 @@ This directory is a separate Composer project on purpose. `voku/agent-loop` is development tooling for this repository, not part of the package contract of `voku/simple-php-code-parser`. Keeping it out of the root `composer.json` avoids raising consumer PHP requirements, leaking agent tooling into downstream installs, and creating first-party dependency cycles such as `agent-map -> agent-loop -> agent-recall-compiler -> agent-map`. -`voku/agent-loop` is also the single root authority for its `agent-*` runtime dependency set. This tool project constrains `agent-loop` directly and keeps `voku/simple-php-code-parser` direct because that is the package under test. The resolved sibling `agent-*` versions are replay evidence, not duplicate root constraints. +`voku/agent-loop` is also the single root authority for its `agent-*` runtime dependency set. This tool project constrains `agent-loop` directly and keeps `voku/simple-php-code-parser` direct because that is the package under test. Resolved sibling `agent-*` versions are replay evidence, not duplicate root constraints. ## Real-issue replay @@ -13,10 +13,12 @@ The first replay uses historical issue #60, `Update for use with PHP 8.4`. The workflow freezes three things before context discovery: - issue title/body and the pre-fix base commit `5156d5d74ca1bce275219f4571efd54ec44be911`; -- `agent-loop 0.16.5` as the release-set authority plus the expected resolved siblings: `agent-kanban 0.3.1`, `agent-learning 0.13.0`, `agent-map 0.8.1`, `agent-recall-compiler 0.13.2`, and `agent-session 0.6.0`; +- `agent-loop 0.16.5` as the direct first-party release-set authority; - agent-skills commit `c7e9d8bdda59d957600bca8dc9f787f03286b277` and the `reproduce-before-fix` L2 recipe. -Before resolution, `verify-release-set.php` fails if the tool project reintroduces direct sibling `agent-*` constraints. After resolution, the same verifier checks the frozen sibling versions against `composer.lock`. +Before resolution, `verify-release-set.php` fails if the tool project or replay input reintroduces sibling `agent-*` version authority. After resolution, the same verifier requires the complete first-party release set to be present in `composer.lock` and reports the versions Composer actually selected. + +If a replay needs byte-for-byte dependency identity rather than compatibility through the frozen `agent-loop` release, commit and install an exact lock file. Do not approximate a lock by copying transitive package versions into another JSON authority. The issue input contains no knowledge of the later fix files. `issue-60-oracle.json` is read only after map search and Recall compilation have finished. @@ -31,4 +33,4 @@ A map miss is recorded as a finding rather than converted into a fake correctnes ## Evidence -GitHub Actions archives the generated tool `composer.lock`, resolved package list, map search output, Recall bundle/facts/system prompt, and post-context evaluation. The lock proves the exact package set Composer resolved for the run; the frozen first-party versions prove that `agent-loop` still resolved the expected coordinated toolchain without a sibling checkout, candidate path repository, or duplicate root pin participating in normal replay evidence. +GitHub Actions archives the generated tool `composer.lock`, resolved package list, map search output, Recall bundle/facts/system prompt, and post-context evaluation. The generated lock and package list record the exact set Composer resolved for that run; `agent-loop` remains the only first-party release-set constraint that the replay owns.