Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 14 additions & 14 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
name: validate

on:
push:
# A TAG push must not start this workflow. `release.yml` calls it through
# `workflow_call`, and both runs land in the same concurrency group
# (`validate-${{ github.ref }}`) — so the push-triggered one cancels the
# release's own call, `release` and `publish` are SKIPPED for a `needs:` that
# was cancelled rather than failed, and the tag ships nothing while the run
# list shows a green validate beside it. Measured 2026-08-20 on
# `agent-stack v0.13.0`: validate green, release cancelled at 2s, npm still
# serving the previous version. It is a race, which is why the two siblings
# with the same shape happened to publish.
tags-ignore: ['**']
# One explicit frozen candidate after local checks and independent review.
pull_request:
# Callable, so `release.yml` gates a publish on THIS suite instead of a
# copy of it. The negative self-tests live here as steps; running them
# from the release workflow any other way means a second home per plant.
types: [ready_for_review]
schedule:
- cron: '0 23 * * *'
timezone: Europe/Warsaw
workflow_dispatch:
inputs:
candidate_reason:
description: 'Recorded explicit candidate exception with task and frozen SHA'
required: true
type: string
workflow_call:

permissions:
Expand All @@ -41,6 +38,9 @@ jobs:
- name: Structural validator
run: python3 test/validate.py

- name: Owner board and release guard plants
run: python3 test/member_owner_gates_test.py

- name: Audit regressions (sherlock plan, one file per closed finding)
run: npm run test:audit

Expand Down
11 changes: 11 additions & 0 deletions docs/HANDOFF.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Current work — R30 owner gates

Objective: close agent-stack's missing own-board/release-ancestry checks under the
family's original R30. [Bounded packet and checks](evidence/runs/2026-10-13-r30-owner-gates.md)
is the entry for the next agent. Branch `fix/r30-owner-gates-1013`, basecb8f699.
Eight baseline defects reproduced; candidate16 controls pass after two orphan-row review corrections; independent review and local aggregate pass at7aa4dd3;
hosted integration and publication remain pending. No installed payload changes.

Exact next task: frozen draft-to-ready candidate with genuine hosted checks,
then normal exact-head merge. Preserve nightly CI and normal required
checks. Commit/push and verify exact release source before hub re-pin/install.
3 changes: 2 additions & 1 deletion docs/evidence/backlog.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ row per line, appended, so two runs closing two rows do not land on the same lin
| B-157 | **The back-links to the Fabric adapter run one way.** agent-interop now points to `fabric-agent-adapter`'s `creating-fabric-agents`, `adapting-projects-to-fabric` and `building-fabric-services`; of those, only `adapting-projects-to-fabric/SKILL.md:25-26` points back (wire questions). The adapter belongs to the Fabric owner: ask there for a pointer from the other two to agent-interop's tier doctrine. Nothing contradicts today — the adapter carries its own copy of the tier rule (`delegation.md:47-55`, 0.20.0). | wave 4 seam reading, 2026-10-12 | 1 | 0 | 1 | **1** | **open** — an ask to the Fabric adapter's owner |
| B-158 | **The residue-ledger check misses an aliased temp-dir call.** `TEMP_MAKERS` (`test/validate.py`) excludes every dotted receiver but `tempfile.`, so `import tempfile as tf; tf.mkdtemp()` and `from tempfile import mkdtemp as m` pass. make-skill 0.31.0 ported this check and closed the gap there (any receiver, renamed from-imports refused, two negative controls); port that back. No current file here uses an alias. | make-skill W4-B seam reading, 2026-10-12 | 1 | 0 | 1 | **1** | **closed in v0.27.1 (2026-10-12)** — `TEMP_MAKERS` is make-skill 0.31.0's pattern (any single receiver, one-line renaming from-import refused); the alias and renamed-import negative controls in `test/validate.py` were watched failing on the old pattern (`B-158 self-test: a temp-dir call through a module alias (\`tf.\`) was not found`, `… a renamed from-import of a temp-dir maker was not found`), then passed. Evidence: verification.md, v0.27.1 |
| AG-06a | **A temp tree created outside `test/` is still invisible.** `check_temp_trees_go_through_the_residue_ledger` reads this repository's own suite. A tree made by anything else — a script under `plugins/`, a future helper outside `test/` — is not accounted for, and `residue.report()` counts only what `workspace()` handed out. The mechanism catches a *reviewed* bypass; it does not observe `$TMPDIR`. Observing it would mean a run claiming ownership of directories it cannot prove it made, which is the reaping mistake `agent-sync` refuses by design. | AG-06 port, 2026-08-20; plan R28 of the hub's 2026-10-11 integration waves | 1 | 0 | 1 | **1** | **closed in v0.27.1 (2026-10-12)** for the static half: the check now reads every `*.py` the repository ships or runs (`test/` recursively, `plugins/`, `bin/`) with exact-path, exact-count, reasoned exemptions (`test/residue.py` 1, `agent-harness/scripts/audit_agent.py` 2). Its first run refused a real bypass, `test/audit_regressions/instruction_budget.py` line 21, now `residue.workspace()`. Plants: an aliased maker in a plugin script, a renamed from-import under `bin/`, a third maker in `audit_agent.py` — each refused by file and line. What stays true: the check reads source, it does not observe `$TMPDIR`; that half stays with AG-06b's content fingerprint. Evidence: verification.md, v0.27.1 |
| AG-06b | **The 2592 existing trees are reported and left alone.** `find "$TMPDIR" -maxdepth 4 -type f -path '*/copy/sub/b.sh' \\| wc -l` → **2592** on 2026-08-20, of which **16 were added by this run's own verification** — one run of the leaking fixture before the port, and one of the v0.12.0 archive's copy of it. They are plain `tmpXXXXXXXX` and indistinguishable from every other program's, so they are named rather than swept. The identical fixture still leaks in **`seo-aeo-audit`**, which another agent holds; referred, not edited. | AG-06 port, 2026-08-20; plan R28 of the hub's 2026-10-11 integration waves | 2 | 0 | 1 | **2** | **closed in v0.27.1 (2026-10-12)** — the trees ARE identifiable by content, so they are reapable without claiming another program's directory: `python3 test/residue.py --reap-own` (dry run by default, `--apply` to remove) claims only an `mkdtemp()`-named, non-symlink child of the temp root holding exactly one of the fixture's five end-state file sets and its directory set byte for byte, plus optionally its own manifest, unchanged for 24 h; root, age floor and the removal window hardened after the PR #45 reading (verification.md, v0.27.1). `test/reap_own_test.py` 6/6 in a sandbox temp root; a planted permissive reaper (any tree holding `sub/b.sh`) was refused: `near-misses were claimed as own: ['tmpextrab01', 'tmpextraf01']`. Real `$TMPDIR`, dry, 2026-10-12: `0 tree(s) match`; the `find` count is 0 — the 2592 trees are already gone, and both leaking suites (`agent-stack`, `seo-aeo-audit`) now use `residue.workspace()`. Nothing in the real `$TMPDIR` was deleted |
| AG-06b | **The 2592 existing trees are reported and left alone.** `find "$TMPDIR" -maxdepth 4 -type f -path '*/copy/sub/b.sh' \| wc -l` → **2592** on 2026-08-20, of which **16 were added by this run's own verification** — one run of the leaking fixture before the port, and one of the v0.12.0 archive's copy of it. They are plain `tmpXXXXXXXX` and indistinguishable from every other program's, so they are named rather than swept. The identical fixture still leaks in **`seo-aeo-audit`**, which another agent holds; referred, not edited. | AG-06 port, 2026-08-20; plan R28 of the hub's 2026-10-11 integration waves | 2 | 0 | 1 | **2** | **closed in v0.27.1 (2026-10-12)** — the trees ARE identifiable by content, so they are reapable without claiming another program's directory: `python3 test/residue.py --reap-own` (dry run by default, `--apply` to remove) claims only an `mkdtemp()`-named, non-symlink child of the temp root holding exactly one of the fixture's five end-state file sets and its directory set byte for byte, plus optionally its own manifest, unchanged for 24 h; root, age floor and the removal window hardened after the PR #45 reading (verification.md, v0.27.1). `test/reap_own_test.py` 6/6 in a sandbox temp root; a planted permissive reaper (any tree holding `sub/b.sh`) was refused: `near-misses were claimed as own: ['tmpextrab01', 'tmpextraf01']`. Real `$TMPDIR`, dry, 2026-10-12: `0 tree(s) match`; the `find` count is 0 — the 2592 trees are already gone, and both leaking suites (`agent-stack`, `seo-aeo-audit`) now use `residue.workspace()`. Nothing in the real `$TMPDIR` was deleted |
| B-159 | **`seo-aeo-audit` still has the AG-06a/B-158 hole.** Its temp-tree check reads less than the whole repository and misses aliased calls, and `test/negatives.py:195` makes a bare `tempfile.mkdtemp(prefix="tp-negatives-snap-")` outside any residue ledger (read on its `origin/main`, 2026-10-12). Port agent-stack 0.27.1's whole-repository scan, any-receiver pattern and exact-shape exemptions there, and route that snapshot through the ledger. | agent-stack PR #45 blind reading, 2026-10-12 | 1 | 0 | 1 | **1** | **open** — another repository; referred, not edited |
| B-160 | **make-skill's temp-tree check reads `test/` only.** make-skill 0.31.0 has the any-receiver pattern but walks `test/` alone, so a maker in a shipped script is invisible there exactly as AG-06a described here. Port agent-stack 0.27.1's whole-repository scan with exact-path, exact-count, exact-shape exemptions back to make-skill. | agent-stack PR #45 blind reading, 2026-10-12 | 1 | 0 | 1 | **1** | **open** — another repository; referred, not edited |
| B-161 | **The owner validator accepted duplicate declarations and a disabled release ancestry check.** Eight real-validator plants passed on cb8f699; the same seam also hid an even-backslash Markdown pipe in AG-06b. | hub R30, 2026-10-13 | 2 | 0 | 1 | **2** | **open** — implementation candidate and16 process controls; independent review, integration and release pending. [Packet](runs/2026-10-13-r30-owner-gates.md) |

## Open, and why

Expand Down
8 changes: 8 additions & 0 deletions docs/evidence/runs/2026-10-13-r30-owner-full.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"source": "7aa4dd36518bc853013aed775ccfe18dcec98a85",
"command": "npm test",
"exit_code": 0,
"log_sha256": "85d0cf29551ffe86b373c5a69c7a2131dcfff51ad64a1c48259b0342138faec9",
"log": "2026-10-13-r30-owner-full.log.gz",
"scope": "Local source validation; hosted integration and publication separate"
}
Binary file not shown.
78 changes: 78 additions & 0 deletions docs/evidence/runs/2026-10-13-r30-owner-gates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# R30 owner board and release gates — 2026-10-13

Base: `cb8f6993a61473f3974bc3af67925439ed4abb75`, version0.27.1.
Owner: agent-stack, leaseR30-GATES-1013. This is a bounded implementation packet
within the hub's original R30/B108/B109, not a closure of other members' gates.

## Contract and reproduction

The owner validator must refuse a malformed or duplicate declared board identity
and a release workflow whose tag ancestry guard can be bypassed. Preserve the
board's composite historical IDs, explicit umbrella namespace and summary table.
Use the already independently reviewed shared release parser, with no shell/YAML
semantic claim beyond its accepted structural grammar. Preserve release gates and
follow the current operator's nightly/explicit-candidate hosted-check policy.

Before the edit, the real `test/validate.py` passed at the base. Eight isolated
process plants then each returned0: duplicate declaration, missing board, missing
header, absent ancestry command, shallow checkout, conditional guard, ignored
failure and early successful exit. The test expected exit1 with the intended
reason and reported8failures. Owned failure copies remain in the residue ledger;
private temporary paths are not copied here.

## Implementation and controls

`test/member_gates.py` is byte-identical to telegram-dev's shared helper at
`711576f89d06525ee94ad4aac0f779a5baeefdd5`; only its release parser and Markdown
cell/label primitives are used. The board adapter is member-specific: one primary
8column declaration table; grouped IDs expanded for duplicate detection; explicit
umbrella IDs kept in a separate namespace. The3column summary is a reference,
not a second declaration. Unsupported primary header shapes fail closed.

The first port found AG-06b's historical command cell contained an even number
of backslashes before a pipe, making9 rendered cells. Its Markdown escape now
keeps8 cells without changing the displayed command. This correction is included
in the same board-contract change.

`python3 test/member_owner_gates_test.py` now passes14 real-process controls:
11 defects plus3 valid controls. Added probes cover composite-ID overlap, the
real even-backslash defect and a missing separator; valid controls retain the
summary, distinguish hub/local IDs and accept bold formatting. Every mutation
asserts changed bytes. The suite requires the expected diagnostic as well as
exit1; it is wired into npm test and the hosted workflow. Its14 owned trees were
removed on successful completion; failures remain attributable by residue.

## Delivery and bounds

Independent review and full local/hosted gates remain pending. No version, tag,
registry or installation is changed. The new nightly schedule is23:00Warsaw;
only deliberate ready-for-review promotion runs an explicit candidate. Routine
push/open/synchronize full-suite triggers are removed under operator policy.

Exact next task: independently review this candidate and its parser boundaries,
then run the owner full gate once, promote a frozen draft PR, integrate normally
and include it in a reviewed patch release. Do not advance the hub pin from this
unreleased branch.

## Independent review correction

At63c54b9 the peer inserted a blank line or HTML comment before B-159 and
then duplicated its identity in B-160. Both real validators returned0: the
adapter stopped before the later rows. Two added process controls reproduced
these failures locally. The guard now refuses ID-shaped orphan rows outside
the primary declaration table, except the explicitly identified3column summary.
The expanded suite passes16 controls with16 owned trees removed. This is a
new source revision requiring peer readback; earlier14PASS is historical.

## Final bounded review and local aggregate

Independent peer review of7aa4dd36518bc853013aed775ccfe18dcec98a85 passed:
16process controls replayed, plus5 independent actual-validator negatives
(blank/comment orphan rows, grouped overlap, formatted duplicate and duplicate
header). The shared helper remained byte-equal to its cited canonical source.

`npm test` then passed at that exact source:17 structural checks,9 plant-guard
cases,16 owner controls,13 reap cases,11 installer cases and all audit regressions.
[Full receipt](2026-10-13-r30-owner-full.json) and compressed output bind the
checked source. This later documentation update does not retroactively change
that source identity. Genuine exact-head hosted CI and normal merge are next.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@ssheleg/agent-stack",
"version": "0.27.1",
"scripts": {
"test": "python3 test/validate.py && python3 test/plant_guard_test.py && python3 test/reap_own_test.py && node test/installer_test.js && npm run test:audit",
"test": "python3 test/validate.py && python3 test/plant_guard_test.py && python3 test/member_owner_gates_test.py && python3 test/reap_own_test.py && node test/installer_test.js && npm run test:audit",
"test:audit": "for t in test/audit_regressions/*.py; do python3 \"$t\" || exit 1; done"
},
"description": "Production patterns for AI agent orchestrators \u2014 tool-calling loops, multi-stage pipelines with checkpoints, LLM provider routing with fallback, four-layer memory with confidence decay \u2014 plus the wallet side of reselling LLM access. This package is the installer CLI.",
Expand Down
Loading
Loading