diff --git a/.agents/skills/backlog-burndown/SKILL.md b/.agents/skills/backlog-burndown/SKILL.md
index bb4c6119..91317aa8 100644
--- a/.agents/skills/backlog-burndown/SKILL.md
+++ b/.agents/skills/backlog-burndown/SKILL.md
@@ -114,7 +114,9 @@ handoff chain `AGENTS.md` "Session Scope" defines, so it records work to do next
its own, and an open one is present by design for as long as that chain is in use. Counting it
inflates the number this run reports as the backlog by one for every lane in use, and a backlog
count this fleet reports wrong is a failure with its own history, so filter the label out of the
-ranking and out of every count of the open backlog rather than out of the ranking alone.
+ranking and out of every count of the open backlog rather than out of the ranking alone. A link
+carrying `blocked` as well, as a parked lane does, is still a link, so this rule controls over the
+`blocked` rule below and the link is neither ranked nor counted.
An issue carrying the `blocked` label is counted and is not ranked while its blocker stands. It
records real work this repository owes, which is why it stays in the count, and the label says the
diff --git a/.agents/skills/dotnet-codestyle/SKILL.md b/.agents/skills/dotnet-codestyle/SKILL.md
index 7eb20897..b13de18d 100644
--- a/.agents/skills/dotnet-codestyle/SKILL.md
+++ b/.agents/skills/dotnet-codestyle/SKILL.md
@@ -214,7 +214,7 @@ parameterized tests. A test project on `xunit.v3` 4.0.0 or later is MTP-based, a
## Project configuration
-.NET 10.0 target, AOT-compatible (`IsAotCompatible=true`, `VerifyReferenceAotCompatibility=true`),
+.NET 10.0 target, AOT-compatible (`IsAotCompatible=true`, with `VerifyReferenceAotCompatibility=true` only under the `PublishAot` condition),
SourceLink, embedded untracked sources, `InternalsVisibleTo` for test/benchmark access. See
`references/project-config.md` for the full property list.
diff --git a/.agents/skills/dotnet-codestyle/references/project-config.md b/.agents/skills/dotnet-codestyle/references/project-config.md
index 42b8fd1c..658e589c 100644
--- a/.agents/skills/dotnet-codestyle/references/project-config.md
+++ b/.agents/skills/dotnet-codestyle/references/project-config.md
@@ -1,8 +1,14 @@
# .NET Project Configuration
1. **Target framework**: .NET 10.0 (`net10.0`).
-2. **AOT compatibility**: `true`,
- `true`.
+2. **AOT compatibility**: `true` unconditionally, and
+ `true` only in a
+ ``, placed in the `.csproj` after it sets
+ `PublishAot` or in `Directory.Build.targets`, never in `Directory.Build.props`, which is imported
+ before the project body and so never sees a `PublishAot` the `.csproj` sets. Reference
+ verification reports `IL3058` for every referenced assembly that lacks `IsAotCompatible` metadata
+ set to `true`, and `TreatWarningsAsErrors` turns that into a failed build on any such
+ dependency, so it runs only where an AOT publish needs it.
3. **Assembly information**: use semantic versioning, include SourceLink
(`true`), embed untracked sources
(`true`).
diff --git a/.agents/skills/session-handoff/SKILL.md b/.agents/skills/session-handoff/SKILL.md
index d0504cbc..d5eda1ae 100644
--- a/.agents/skills/session-handoff/SKILL.md
+++ b/.agents/skills/session-handoff/SKILL.md
@@ -143,8 +143,9 @@ section is the pointer to what to read rather than the answer, and `AGENTS.md` "
already says stale context is worse than absent. A handoff is context by construction, so this is
that rule applied to the one artifact built to outlive the session that wrote it.
-Read the current link's comments too, with `gh issue view "" --comments`, since `resume` prints
-only the body, and a parking comment or a closing session's answers land in the comments.
+Read the current link's comments too, with `gh issue view "" --repo OWNER/NAME --comments`, since
+`resume` prints only the body, and a parking comment or a closing session's answers land in the
+comments.
Read the chain before re-attempting anything. `resume` prints the current body and indexes the
closed links behind it, and `chain --grep` searches the bodies it walks for a regular expression,
@@ -161,7 +162,9 @@ rather than `resume`.
Re-derive a count rather than copying one, and read it with an explicit page size. `gh issue list`
returns 30 rows unless told otherwise, and a truncated count reads exactly like a repository with
-30 issues, which is worse than an absent count because it gets stated.
+30 issues, which is worse than an absent count because it gets stated. The same holds at any page
+size, so where a read returns as many rows as its limit, raise the limit and read again until it
+returns fewer.
## The Attended Session
@@ -169,15 +172,17 @@ A maintainer resuming work says little, often only "resume the handoff", and tha
the whole procedure below. Run every step without being reminded of any of them.
1. **Pick the link.** Where the maintainer names an issue, take it. Where none is named, read
- `gh issue list --label handoff --state open --limit 100 --json number,title,labels,updatedAt`,
- since `tracks` prints neither labels nor exact update times. Take a link carrying `blocked`
- first, newest update first among them, since its blocker is a decision only the maintainer can
- make and the maintainer is now present. Otherwise take the newest update among the rest. An
- `auto-*` link not carrying `blocked` is skipped unless named, since an `unattended-handoff`
- worker may hold it right now, and where one is named, confirm with the maintainer that no
- unattended run is live before working it. Where every open link is skipped, say so and ask the
- maintainer which to take. Say which link was picked in one line before anything else, so a wrong pick costs one reply
- rather than a round.
+ `gh issue list --repo OWNER/NAME --label handoff --state open --limit 100 --json
+ number,title,labels,updatedAt`, since `tracks` prints neither labels nor exact update times.
+ Where it returns as many rows as the limit, the list may be truncated, so raise the limit and
+ read again until it returns fewer, rather than picking from a partial list. Take a link carrying
+ `blocked` first, newest update first among them, since its blocker is a decision only the
+ maintainer can make and the maintainer is now present. Otherwise take the newest update among the
+ rest. An `auto-*` link not carrying `blocked` is skipped unless named, since an
+ `unattended-handoff` worker may hold it right now, and where one is named, confirm with the
+ maintainer that no unattended run is live before working it. Where every open link is skipped,
+ say so and ask the maintainer which to take. Say which link was picked in one line before
+ anything else, so a wrong pick costs one reply rather than a round.
2. **Resume it** per "Resuming" above, comments included, re-deriving live state rather than
trusting the body.
3. **Ask what it is blocked on first.** Where the link carries `blocked`, the parking comment names
diff --git a/.agents/skills/unattended-handoff/SKILL.md b/.agents/skills/unattended-handoff/SKILL.md
index b6bf958c..7fd62266 100644
--- a/.agents/skills/unattended-handoff/SKILL.md
+++ b/.agents/skills/unattended-handoff/SKILL.md
@@ -150,23 +150,24 @@ does not qualify, since skipping one costs nothing and a guess costs a revert an
lacks.
- **It reverses no settled decision** recorded in an issue, a handoff, or the rule text.
- **Nothing has worked it or is working it.** The track `auto-` has no link, open or closed,
- which `handoff.py chain --track "auto-" --limit 1` answers with its refusal naming no
- handoff on that track. Any other refusal from it is a `STOP` rather than a yes. No open pull
- request names it, and no pull request whose squash commit is in `origin/main..origin/develop`
- names it anywhere in its body, since a fix merged to develop leaves its issue open until it is
- promoted, whoever merged it. No open handoff on any track names it in its next steps, and no
- comment on it claims it for a `backlog-burndown` group, since both mark work that has no pull
- request yet.
+ which `handoff.py chain --repo "/" --track "auto-" --limit 1` answers with its
+ refusal naming no handoff on that track. Any other refusal from it is a `STOP` rather than a yes.
+ No open pull request names it, and no pull request whose squash commit is in
+ `origin/main..origin/develop` names it anywhere in its body, since a fix merged to develop leaves
+ its issue open until it is promoted, whoever merged it. No open handoff on any track names it in
+ its next steps, and no comment on it claims it for a `backlog-burndown` group, since both mark
+ work that has no pull request yet.
## The Picker
1. **Check the promotion first** under `main` or `release`. Where an open `decision` issue names
the open develop -> main pull request, return `STOP` before picking anything, since every worker
this run dispatched would meet that same decision after merging its own work to develop.
-2. **Read the open handoffs** with labels and update times, `gh issue list --label handoff --state
- open --limit 100 --json number,title,labels,updatedAt`, since `handoff.py tracks` prints
- neither. Reach `scripts/handoff.py` from a hub checkout, per `session-handoff` "Running the
- Chain".
+2. **Read the open handoffs** with labels and update times, `gh issue list --repo "/"
+ --label handoff --state open --limit 100 --json number,title,labels,updatedAt`, since `handoff.py
+ tracks` prints neither. Where it returns as many rows as the limit, the list may be truncated, so
+ raise the limit and read again until it returns fewer, rather than ranking a partial list. Reach
+ `scripts/handoff.py` from a hub checkout, per `session-handoff` "Running the Chain".
3. **Prefer an open `auto-*` handoff not carrying `blocked`**, oldest first. That is a lane an
earlier run parked and the maintainer has since unblocked, or one whose worker died, and a live
link is work already framed. Handoffs on any other track belong to the maintainer's attended
@@ -175,23 +176,27 @@ does not qualify, since skipping one costs nothing and a guess costs a revert an
the session handing the lane back removes the label, per `GOVERNANCE.md` "Durable Knowledge and
Self-Improvement".
4. **Otherwise pick from the backlog.** Rank the open issues by `backlog-burndown`'s "Ranking"
- criteria, keep the auto-resolvable ones, and take the top one. Read the list with an explicit
- page size, since `gh issue list` returns 30 rows unless told otherwise.
-5. **Create its handoff** with `handoff.py new --track "auto-"`, `--dry-run` first. The body
- carries the sections `session-handoff` "What Goes in the Body" names, with the next steps naming
- the issue and what done looks like. That skill's rules on the body bind it.
+ criteria, keep the auto-resolvable ones, and take the top one. Read the list with `gh issue list
+ --repo "/" --state open` and an explicit `--limit`, since it returns 30 rows unless
+ told otherwise, and apply step 2's full-page check to it.
+5. **Create its handoff** with `handoff.py new --repo "/" --track "auto-"`,
+ adding the `--title` and `--body-file` it also requires, as `session-handoff` "Running the Chain"
+ shows, and `--dry-run` first. The body carries the sections `session-handoff` "What Goes in the
+ Body" names, with the next steps naming the issue and what done looks like. That skill's rules on
+ the body bind it.
6. **Choose the worker's tier** by `backlog-burndown`'s "Choosing the Worker's Model Tier".
7. **Reply with one line.** A picker writes nothing but the handoff it creates, and returns `STOP`
where a read it needs cannot run.
## The Worker
-1. **Resume the handoff** with `handoff.py resume --track "