LearnNewsExamplesServices
Frontmatter
id15894
titleticket-intake and epic-review fire on their own author — carve by drift, not by age
stateClosed
labels
enhancementairefactoringmodel-experience
assigneesneo-opus-grace
createdAtJul 25, 2026, 12:08 PM
updatedAtJul 25, 2026, 5:49 PM
githubUrlhttps://github.com/neomjs/neo/issues/15894
authorneo-opus-grace
commentsCount0
parentIssue13652
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJul 25, 2026, 5:49 PM

ticket-intake and epic-review fire on their own author — carve by drift, not by age

Closed Backlog/active-chunk-9 enhancementairefactoringmodel-experience
neo-opus-grace
neo-opus-grace commented on Jul 25, 2026, 12:08 PM

Context

Operator friction item, 2026-07-25, raised as an explicit friction→gold prompt: "friction→gold means to not take everything 'valid as written', but to keep an eye out on what makes sense, and what does not."

Two observed instances:

  1. ticket-intake fired on a ticket its own author wrote minutes earlier. Happened in this session: #15891 was authored through ticket-create's full six-stage challenge chain, then picked up ~20 minutes later in the same context window, where intake's trigger ("immediately when assigned a new ticket") nominally demands a 31,608-byte re-validation of the author's own fresh reasoning.
  2. epic-review fired on a self-authored epic. Operator-observed: a session graduated an ideation sandbox → epic → subs, then the same author, in the same context window, ran epic-review on their own epic before starting a sub.

Both gates are consumption-side duals of a creation-side gate that already ran: ticket-createticket-intake, epic-createepic-review. They were designed for the cross-author and stale-artifact cases and are currently applied reflexively to the self-author, same-session case, where they re-derive reasoning that is still live in context.

The Problem

Neither trigger carries an authorship or freshness carve

Verbatim from the SKILL.md frontmatter:

  • ticket-intake: "Use this skill immediately when assigned a new ticket, before checking out a branch or writing any codebase modifications."
  • epic-review: "Use this skill when an agent is about to pick up its first sub from an unreviewed epic (per model-identity)."

epic-review's "per model-identity" clause actively guarantees the pathology: the epic's author is a model identity, so authoring an epic and then picking up its first sub triggers a self-review by construction.

Cost of a reflexive invocation

Gate Bytes ~Tokens
ticket-intake 31,608 ~8,780
epic-review 25,370 ~7,040
pair 56,978 ~15,800

Against the Codex flatrate window (~258K before compaction) a reflexively-invoked pair is ~6% of the window. Measured this session, ~90% of token cost is re-reading context, so a payload loaded once is paid again on every subsequent message and again after every compaction.

The operator's proposed rule is falsified by the operator's own caveat

The proposal was "same author, less than 24h old, no need to use it", paired with the caveat "old is a tricky pony when we merge 20-40 PRs a day." The caveat wins. Measured on origin/dev:

Window Commits Rate
last 1d 41 41/day
last 3d 90 30/day
last 7d 207 29/day

24 hours is 30–40 merges of drift. Wall-clock age is a weak staleness proxy in this repo. The right question is not how old is the ticket but how much moved underneath it.

The Architectural Reality

  • .agents/skills/ticket-intake/SKILL.md + references/ (31,608B) — the Pre-Execution Reflection Gate; creation-side dual is ticket-create.
  • .agents/skills/epic-review/SKILL.md + references/ (25,370B) — six-stage pre-work epic gate, "per-agent-per-epic one-shot"; creation-side dual is epic-create.
  • .claude/CLAUDE.md §edge_case_triggers carries "Ticket Creation Freshness: Before any create_issue, invoke ticket-create" — the creation side is turn-loaded and unconditional. The consumption side is skill-triggered and is where the carve belongs. Turn-loaded substrate is Tier-4 and out of scope for an agent-authored change; this ticket touches only the two skills.
  • ADR 0008 §2.1 governs the frontmatter description contract that both triggers live in; the carve is a trigger-language change, so it is a Map edit, not new payload. create-skill consulted per ticket-create §1b.
  • Sibling precedent for narrowing rather than deleting a gate: #15891 / PR #15893 (post-review-pickup 20,396 → 7,852B), same operator directive lineage.

The Fix (direction — implementer refines)

Make both gates authorship- and drift-aware, keyed on externally-checkable facts rather than wall-clock age.

Proposed three-tier carve, applied to both skills:

Case Gate behavior
Different author Full gate, unchanged. This is what the gates were built for and the change must not weaken it.
Self-authored, same session Exempt. The creation-side dual ran in this context window and its reasoning is still live. Re-running the consumption-side dual re-derives the author's own last hour.
Self-authored, different session Narrowed probe, not the full payload. The reasoning is gone from context, but the artifact is still yours — so run the drift check only, and escalate to the full gate only if it fires.

The drift probe, mechanical and cheap:

git log origin/dev --since="<ticket createdAt>" --name-only

intersected with the paths the ticket declares in its Architectural Reality / Fix sections. Zero intersection = reality did not move underneath this ticket, and intake's core question ("did Neo reality change?") is answered no without loading 31KB. A non-empty intersection is exactly when the gate earns its cost, and it names the specific files to re-check.

Why this beats the 24h rule: it measures the thing the gate actually cares about. A ticket 3 days old whose surface nobody touched is fresher than a 6-hour-old ticket sitting under 15 merges.

Every exemption input is externally checkable — session identity, GitHub issue author, and git log — so the carve cannot become a self-declared loophole. That is the load-bearing constraint: an agent must not be able to talk itself out of a gate.

Additionally: epic-review's "per model-identity" clause should read "per model-identity other than the epic's author", which alone removes the operator-observed instance.

Acceptance Criteria

  • ticket-intake and epic-review frontmatter triggers carry the authorship carve; both remain within ADR 0008 §2.1's Map contract and the router byte budget.
  • epic-review's "per model-identity" clause excludes the epic's own author.
  • The self-authored/different-session drift probe is specified concretely enough to run without loading the full payload (the git log shape above or better), and lives in the Map, not behind another payload read.
  • The different-author path is byte-identical in behavior; a fixture or worked example demonstrates it is untouched.
  • Net .agents/skills Markdown delta is negative or pointer-sized; node ai/scripts/lint/lint-skill-manifest.mjs --base origin/dev green.
  • The exemption is documented as externally-checkable-only, with the self-declaration failure mode named explicitly so a future reader cannot widen it into "I judged it fresh".
  • A cross-family reviewer explicitly checks the loophole surface, not just the byte reduction.

Out of Scope

  • Turn-loaded substrate. .claude/CLAUDE.md §edge_case_triggers' "Ticket Creation Freshness" line is Tier-4 operator authority; this ticket does not touch it. If the carve implies a turn-loaded edit, surface it rather than making it.
  • The creation-side gates. ticket-create and epic-create stay unconditional — the carve exists precisely because the creation-side gate ran.
  • Shortening the two payloads' content. That is the #15891-class lane and should be a separate ticket per skill; this one changes only when they fire.
  • Other pre-flight gates (structural-pre-flight, turn-memory-pre-flight, architecture-pre-flight). They may have the same shape, but each needs its own creation-side-dual analysis; bundling would make the loophole surface unreviewable.
  • Any mechanical enforcement of the carve. It is trigger language read by the agent, not a lint.

Avoided Traps

  • The 24h rule as stated. Falsified by the measured 29–41 commits/day; it would exempt tickets sitting under 30+ merges while still gating a week-old ticket nobody's surface moved under.
  • Self-declared freshness. "I judged this ticket still valid" is the loophole shape L3 warns about. Every input here is external: session id, issue author, git log. If an implementer finds themselves adding a judgment call, that is the signal the carve is being widened.
  • Deleting the gates. The cross-author case is the majority case and the gates are load-bearing there — a stale ticket picked up by a different agent is exactly how wrong work gets built. This narrows the trigger; it does not weaken the gate.
  • Exempting on authorship alone. A ticket you wrote three weeks ago is not fresh — your reasoning is gone from context, which is precisely when intake pays. Session-scope is doing the real work in tier 2, not authorship.
  • Assuming same-session implies same-context. After a compaction the author's reasoning may be as gone as another agent's. If a cheap compaction signal is available, tier 2 should key on it; if not, note the residual honestly rather than pretending session-id is a proxy for context continuity.

Decision Record impact

aligned-with ADR 0008 — the change is trigger language in the SKILL.md Map, within the §2.1 frontmatter contract. No ADR is amended or challenged.

ADR successor-risk: adr-aligned — artifact dated 2026-07-25; ADR 0008 governs skill-shape and this is a Map-level trigger edit; evidence = both SKILL.md frontmatter blocks quoted above; route continue.

Related

  • #13652 (Epic — mechanical enforcement replaces prompt-machinery; skill cadence is named in its intended solution) — candidate parent.
  • #15891 / PR #15893 — sibling reduction under the same operator directive; precedent for narrowing substrate rather than deleting it.
  • #15892 — the ADR-0019 twin/export amendment from the same review session; unrelated surface, same friction→gold lineage.
  • ticket-create §1b Meta-Skill Sweep and ADR 0008 §2.1 — the gates this ticket's own implementation must satisfy.

Live latest-open sweep: checked latest 20 open issues at 2026-07-25T10:07Z plus a semantic search over all states for intake/exemption/freshness-gate scope — nearest hits (#12990, #15790) are both CLOSED and unrelated; no equivalent found. A2A in-flight claim sweep at 2026-07-25T09:51Z, all read-states: no claim on skill-trigger scope; newest peer traffic is 2026-07-25T00:37Z (~9.5h stale, roster parked by operator).

Origin Session ID: cb8b1069-54cc-43fd-943f-0e20b45661c8

Retrieval Hint: query_raw_memories("ticket-intake epic-review self-authored same-session exemption drift probe not wall-clock age")

Authored by Grace (@neo-opus-grace, Opus 5)


Amendment 1 (2026-07-25) — authorship was the wrong axis

@tobiu raised the case that breaks the three-tier carve above: different author, but hot context. An ideation-sandbox graduation you participated in, where a peer authors the epic and its subs. Authorship says "different author → full gate"; context says "I was there."

The V-B-A inverts the intuition, so the carve does not widen here. Reading what epic-review actually checks (epic-review-workflow.md):

Stage What it checks What a discussion participant holds
1 — Roadmap Fit does the epic fit the roadmap mostly agreed in the discussion — cite, don't re-derive
2 — Approach Elegance is the approach right same
2.5 — Source Discussion Criteria Mapping "Has the Epic dropped any graduation criteria established in its source Discussion?" — mapping presence, Decision Record preservation, mapping completeness; unexplained deferrals are failures the participant is the best-qualified reviewer alive for this stage
3 — Sub-Structure Coherence coverage, overlaps, phase boundaries, missing phases, scope creep, structural pre-flight nothing — the peer's decomposition
3.1 — Evidence Matrix seeding per-AC L1–L4 evidence classification nothing
4 — Prescription Layer is each sub at the right layer nothing
5 — Avoided Traps completeness of rejected alternatives partial

Stage 2.5's own first step is "Fetch the source Discussion body and extract the Graduation Criteria." A participant already holds that, so the highest-value stage becomes nearly free — while Stages 3–4, the peer's independent transformation of discussion → epic → subs, are entirely unseen regardless of participation.

Conclusion: participation lowers the COST of the most valuable stage; it does not remove the need for the review. The correct handling is narrowing and re-ordering, not exemption:

  • Stages 1–2: cite the discussion position instead of re-deriving it (link the comment, one line).
  • Stage 2.5: run it — from context, no Extraction round-trip.
  • Stages 3 / 3.1 / 4 / 5: run in full. Unseen work.

The corrected general axis

Authorship was a proxy. The real question is what have you already seen — so the carve scopes to the unseen, not to who typed it:

What you hold Gate behavior
the artifact AND its reasoning (self-authored, same session) exempt
the inputs but not the transformation (discussion participant, peer-authored epic) run the stages covering the transformation; cite the rest
neither full gate

This subsumes both observed cases and is still externally checkable: authorship and session identity from the artifact and the session id; participation from the Discussion's own comment thread (did you post in it?), which is a public fact, not a self-assessment.

AC deltas from this amendment

  • The carve is expressed on the seen/unseen axis above, not on authorship alone; authorship and participation are inputs to it, not the rule itself.
  • epic-review gains the participant path: Stages 1–2 citable, 2.5 run-from-context, 3–5 full. It must NOT be expressible as "I was in the discussion, so I skip the review".
  • Participation is evidenced by a linked Discussion comment authored by the reviewer — never a self-declared "I was involved".

Trap added

  • Reading "hot context" as "already reviewed". The discussion is the input to the epic; the epic is a transformation of it. Holding the input tells you nothing about the transformation, and Stage 3's whole purpose is checking the transformation. An agent that exempts itself here would be skipping the only stages it was never qualified to skip.