Skip to content

Inline technique calls: composition inside a group, an activity step across one - #466

Draft
m2ux wants to merge 20 commits into
mainfrom
feat/397-handling-inline-techniques
Draft

Inline technique calls: composition inside a group, an activity step across one#466
m2ux wants to merge 20 commits into
mainfrom
feat/397-handling-inline-techniques

Conversation

@m2ux

@m2ux m2ux commented Aug 15, 2026

Copy link
Copy Markdown
Owner

Summary

A technique file tells the assistant how to carry out one job, and partway through its instructions it will sometimes say to go and do what another technique file says. This work draws a line through those calls. Where the file being named sits alongside the caller in the same group — a folder of related operations sharing one declared set of inputs — the call stays as it is, because the group is composing itself and nothing outside it can see the arrangement. Where the call reaches out of the group, it becomes a step in an activity, because that is now the only boundary in the system where the values handed over are declared and checked.

There are 223 such calls, 87 of them inside a group and 136 leaving one. The 87 stay. The 136 are the population this work moves, checks, and then keeps out.

🐛 Issue 📐 Engineering


Motivation

The server that hands out technique files treats a sentence naming another file as ordinary text. It does not go and find the file, it does not compare the values being handed over against the ones that file declares it needs, and it does not deliver that file alongside the first. The assistant is given a path buried in the middle of a sentence and works out the rest on its own; where the path resolves to nothing, it either skips the instruction or improvises, and nothing in the run records which. That drift is already in the tree — renaming a technique, or changing the values it needs, reaches its callers silently. Eleven entries in the server's core-operations list exist only to compensate for the missing delivery by hand.

What changed while this was open

Two pieces of work landed on main that move the ground under it.

The first gives an activity a variable contract. An activity now declares the session values it reads and the values it writes, the writes are contributed to the workflow when the activity joins its graph, and a checker walks the graph for a read nothing writes, a write nothing reads, and a read that some path reaches before any write. 117 of the 122 activity files carry a contract and the check is green.

The second moves routing off the activities and into the workflow. An activity names its outcomes and nothing about what follows; the workflow file states where each outcome leads. Route declarations remaining in activity files: zero. That matters here because it changes the price of splitting an activity in two, which used to cascade into every workflow that borrowed it and is now an edit to one workflow file.

Together those two make the activity boundary the one place in the system where a value crossing between two units of work is declared, checked, and provably written before it is read. That is exactly what an inline call has always lacked. So the question stopped being how to make inline calls checkable and became which of them should exist at all.

The measurement that draws the line

Splitting the 223 calls by whether they cross a group boundary produces a result that had not been looked at before:

calls sites handing over a value the caller cannot supply
Alongside a sibling in the same group 87 (39%) 41
Reaching out of the group 136 (61%) 73

The sibling half is concentrated in the shared operation groups: the workflow engine's own operations call each other 27 times, the GitHub command-line group 17, the graph-query group 16, the Rust build group 6.

Those 87 must not move, and moving them would be worse than leaving them. An operation in a shared group is bound as a step by many different workflows. Hoisting a call it makes to one of its own siblings means every one of those workflows gains that sibling as a preceding step — the group's internal ordering published into a dozen workflow files, each holding a copy of a sequence it does not own. That is the same fault the routing work was written to remove, and paying it here to buy uniformity would be a poor trade.

The 136 crossings are the opposite case. Each one reaches a contract written somewhere else, with nothing binding the values and nothing checking them; 73 of them hand over a value the caller has no way to supply. Those are the calls the activity boundary can now take, and check.

One number from the original framing does not survive and is worth stating plainly. This was opened against 118 call edges across 554 technique files. That count reproduces under no counting rule that has been tried, varying by 59% across definitions of "a call" that read identically in prose. Acceptance is not keyed to it. It is keyed to the boundary rule, which needs to answer one question about each call — does it leave the group — and not to a precise total.


Changes

Stage one — the boundary rule. The checker compares the group the calling file belongs to against the group the called file belongs to. Staying inside is composition and passes. Leaving is a defect and fails. This replaces ten separate counting terms, each of which existed to bin and price calls precisely enough to deliver them; a boundary question needs none of that, and none of the coverage caveats that came with it. A call written in prose with no link still evades the check, as it always did, and the check says so rather than reporting clean over ground it never read.

Stage two — repair the crossings inside the groups. 41 sibling calls hand over a value the caller cannot supply. Each is fixed inside its own group, by adding the value to the group's declared inputs or to the calling operation's own signature. No consuming workflow is involved.

Stage three — move the crossings out. The 136 calls that leave a group become activity steps. The value each one carried becomes a variable the activity declares, so the load checks that something writes it and that the write precedes the read on every path. This is batched by group, largest first, and each batch is verified by a check that is already green on main.

Stage four — retire the delivery machinery. The walk that carried callee bodies to the caller, the three doors it rode on, the ledger entry that collapsed a delivered callee against a scheduled one, the block that attributed a callee's inherited rules to the calling site, and the two-plane counting that reported the same population on two bases — all of it existed to make a crossing work in place. A crossing that becomes a step does not need it. What is kept is the part that finds a link in a protocol and the part that compares two group names.

Stage five — the written authorities. The design canon, the construct inventory and the addressing specification currently disagree with each other and with the running server about whether these calls are legitimate at all. They are brought into agreement on the boundary rule: a call to a sibling is how a group composes itself, a call that leaves the group is an activity step.

Cross-workflow ancestry stays as delivered. A called technique takes its container contracts from its own home tree, applied identically at every resolution site — that is correct whether the call is delivered inline or bound at a step.

Core operations. The eleven hand-maintained entries compensating for the missing delivery are settled by the stage that serves each one: an entry standing in for a crossing retires when that crossing becomes a step, an entry standing in for a sibling call retires against the group that owns it, and an entry still unserved stays and is reported.

Acceptance

  • Every call in a technique protocol either names a sibling of the caller's own group or does not exist; a call leaving a group fails the check.
  • No sibling call hands over a value the caller cannot supply.
  • Every value that used to cross a group boundary inside a protocol is declared by the activity that writes it and by the activity that reads it, and the graph walk finds a writer on every path.
  • The count of calls leaving a group is stated before and after, and after is zero.
  • The check states what it cannot see — a call named in prose with no link — rather than reporting a clean population.
  • The three written authorities state the same rule as the checker.

Not in this change

The obligation to surface risk before an edit. One called operation requires its caller to put a high or critical risk finding to the user before proceeding. It crosses a group boundary and it depends on sitting next to the edit instruction, so as a separate step it has nowhere to live. It needs either a gate the server enforces or a rule on the group container, and that decision is open.

Composite activities, and an activity step whose operation the workflow supplies. These are what would let the repeated pre-check-and-post-check shape be declared once instead of copied per operation, and would give a value hoisted out of a protocol a scope narrower than the whole session. Both are named as unblocked by the routing work and neither is filed. They are the reason the crossings move as ordinary steps here rather than as a reusable wrapper.

Replacing an operation's prose with a typed tool call. Withdrawn before any file was edited, and tracked separately: the acceptance clause and the group's own rule requiring its operations be used cannot both hold.


📌 Submission Checklist

  • Changes are backward-compatible (or flagged if breaking)
  • Pull request description explains why the change is needed
  • Self-reviewed the diff
  • I have included a change file, or skipped for this reason: [reason]
  • If the changes introduce a new feature, I have bumped the node minor version
  • Update documentation (if relevant)
  • No new todos introduced

🔱 Fork Strategy

  • Runtime Update
  • Client Update
  • Other
  • N/A

🗹 TODO before merging

  • Ready for review

Seed commit anchoring the draft PR for the inline-technique handling work
package of epic #397 (protocol structure: alternatives and delegation the
server can see). Implementation follows.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@m2ux m2ux self-assigned this Aug 15, 2026
m2ux and others added 19 commits August 22, 2026 15:48
…e link partition

The grammar module fixes the ten counting terms the inline-reference guard
publishes, and classifies a markdown link destination as a technique
reference, a resource reference, or neither.

Classification keys on the destination's shape rather than on how the author
spelled the path. A destination naming a markdown file outside a resources
tree is a technique reference, so the same target cannot be a technique under
one spelling and a resource under another. Resource claiming delegates to
that classifier: previously a dotless op.md was claimed as a resource id
while the identical ./op.md was not claimed at all, which made a technique
path invisible to the very scan the guard exists to make total.

The verb-case term is fixed case-insensitively on measurement rather than by
assumption: the corpus carries 238 capitalised Apply against 59 lowercase,
and only the case-insensitive reading reproduces the published census of 172
raw occurrences and 135 logical call sites.

The module computes no anchor slug. Heading-anchor resolution stays with the
resource layer, so the tree holds one slug computation and one grammar.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
All three ancestor-resolution sites take the workflow the callee's file was
found in. A cross-workflow or meta-shared callee therefore inherits the
container contracts that govern it where it lives.

Two of the three sites passed the requested workflow id, so a callee reached
by cross-workflow prefix or through the meta fallback composed against the
same-named containers of whichever workflow asked for it — silently
inheriting the wrong contract. Both had the provenance-correct value in
scope on the adjacent line, and the third site already demonstrated the
form. The bundle door now reads its source workflow from
readTechniqueWithSource rather than discarding it.

The four tests covering this all fail against the prior resolution and pass
against the new one, including the both-doors parity case, which is what
establishes that the bundle door carried the defect too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The corpus submodule carries Atlassian's intra-group calls resolved into
their callers and both rule-addressed citations written as dotted
addresses, taking container-targeting qualified pairs to zero.

Signed-off-by: Mike Clay <mike.clay@shielded.io>
The committed walk snapshots all reproduce at the corpus pin this branch
delivers; only the stamp recording which commit they were generated
against still named the previous pin, so the freshness check failed while
every snapshot passed.

Stamping belongs with the commit that moves the submodule. That commit is
already published and the no-amend rule holds, so the stamp lands here on
its own rather than being folded backwards.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The published grammar admits `apply`, `via`, `use`, `follow` and `per` as
invoking verbs, and matches each on a word boundary at both ends so a verb
abutting punctuation counts like any other.

Width is a published term rather than an implementation detail, because it
decides the guard's coverage as well as its total. Measured at the delivered
corpus pin, these five see 88% of the GitNexus cross-group call sites where
`apply` alone sees 30%, and widening to nine verbs buys four further points
for thirteen more deduplicated pairs.

The corpus-wide totals this fixes: 198 logical call sites, 178 deduplicated
pairs, 101 caller files, 73 distinct callees.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nd publishes its coverage

A technique file calls another technique from inside its Protocol prose and
nothing resolved those calls, so a renamed callee stranded its callers in
silence and a call passing fewer arguments than its callee declares read
exactly like one passing all of them. The guard makes the class visible: a
new call site joins a census rather than an unmeasured remainder.

Two classes fail hard, being unambiguous — a destination that names no file,
and a qualified call whose operation half names one of the group's rules,
which is read rather than applied and belongs at a dotted address. Both are
clean at the delivered corpus pin, so no triage file exists; the triage
mechanism is a pure function over a finding set, and its absence on disk is
the statement that nothing has needed a verdict.

Argument conformance and value-named callees are classified rather than
failed. A static reading of the name-match convention cannot see a runtime
variable bag, so those bins are a disposition worklist behind --worklist and
failing on them would have the guard assert what it cannot observe.

The census reports coverage beside every total, because the two are separate
facts: the total is reproducible at any verb-list width, and a guard
reporting clean over a group it never examined must not read as one that did.
It admits 198 of 254 link-resolvable references, 78%. Every figure is keyed
on syntax, so none of them is evidence about a caller that names an
operation in prose — midnight-system-review reaches this corpus's GitNexus
operations nine times across three files with no link and no qualified pair
anywhere. A retirement decision needs a reading pass, never this number.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… escape

The deduplicated-pair key joined caller and callee with a literal NUL byte,
which makes the file binary to grep and git. It is spelled as a unicode
escape now, the form findingKey already uses for the same separator.

The header's class list also names the two classes the guard fails, and lists
the argument bins and value-named callees separately as the worklist it
prints — matching what the code does.

Found by check-source-encoding, which is the guard for exactly this and
caught it on the sweep.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nine corpus guards resolved their root permissively and hand-rolled a
clean-or-findings exit, so neither an absent corpus nor a present-but-hollow
one had any way to reach exit 2. Each answered "nothing wrong" to a question
it could not read, and this package's own worktree is the case that proves
why that matters: the corpus submodule was unprovisioned there, and every one
of the nine reported clean.

measureOrExit is the entry point for a guard that renders its own findings
rather than speaking the finding protocol — it resolves the root strictly,
runs the collector, and exits 2 with the reason when either step finds the
corpus unreachable. runGuard already covered the guards that speak the
protocol; the two together mean every corpus guard has a path to exit 2.

Both halves are needed and both are exercised. A missing root is caught by
requireWorkflowsRoot; a reachable root the guard walks to no effect is caught
by assertScanned, each guard naming the surface it actually inspects. The
module-scope permissive root stays as the default parameter value, so
importing a guard in a test still never throws.

Measured after: 30 guards, 0 unable to report unmeasured.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ten terms, ten fixture corpora, each read by the real guard so a fixture pins
the term as the guard implements it rather than as a test re-implements it.
Each fixture is built so the count under the published answer differs from the
count under the plausible alternative, which is what makes it fail when the
term's meaning changes.

A total cannot show that two of its terms measure the same form, and this
package measured exactly that: container-target and counting-unit both track
the qualified pair. The overlap is asserted rather than described — one case
shows the two moving together on a pair, another shows container-target still
having an effect where no pair exists, which is why both terms are published
instead of one.

The census gains rawLinkOccurrences, the pre-collapse count the counting-unit
term converts into call sites. Without it that term had no assertable
alternative reading, since every other census figure was already post-collapse.

Totals asserted at the delivered corpus commit: 198 logical call sites, 178
deduplicated pairs, 101 caller files, 73 distinct callees, 351 raw link
occurrences, 254 link-resolvable references, 82 collapsed pairs, 78% coverage,
zero unresolved targets and zero findings in either hard class.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The `workflow::group::op` and `group::op` spellings name one operation and
inherit one contract. The leading prefix addresses a workflow rather than a
container, so the ancestor walk starts after it.

Measured on `meta::workflow-engine::dispatch-activity` through the step-bound
door: 22 rule names under either spelling, where the qualified spelling
previously carried 10. The twelve dropped rules were the whole
`workflow-engine` container contract, `verify-dispatched-activity` among them.
The bundle door resolves the prefix correctly already, so the two doors
disagreed on one reference, which SC-1 asserts they cannot.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…aches

A depth-first walk carries a visited set, delivers each referenced body once,
and continues past an edge reaching a body already delivered. Revisit tolerance
is categorical rather than empirical: delivery is a reachability problem, so no
closure member computes a value another member consumes and a body is complete
the first time it arrives.

Each call site resolves against the file it was authored in. A composed body
carries its ancestors' Initial/Final blocks, whose relative links point out of
the ancestor's directory rather than the callee's; the walk therefore reads each
technique's own protocol and reaches an ancestor's calls only when that ancestor
is itself a closure member. Measured at corpus pin `12400e85`: of 76 container
files, 0 carry a technique link in an Initial or Final block and 0 call sites sit
there, so the two readings coincide today.

A qualified pair delivers its operation and never the group container, in both
the two-link and the bare-text spelling.

Measured over the corpus, walking every one of 572 technique files as a root:
230 closure members delivered, 110 revisit edges of which 9 are self-loops, 0
unresolved edges. The live fixture behaves as the plan describes — from
`verify-index`, one member (`analyze`), one delivery event, and five revisits
carrying both the closing edge and the self-loop.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Rules take the own-versus-inherited partition inputs and outputs already take.
A technique's own rules stay under `rules`; the rules its containers impose
arrive under `inherited_rules`, each naming the contract that imposes it — the
workflow-root contract or the group path. Attribution is the winning
definition's, so an inner container overriding an outer one is credited with the
rule that governs.

That block is the mechanism the design rests on rather than a refinement on top
of it. A folded callee is delivered as the operation body without the container
bodies around it, so an unattributed merged map is the point at which the
callee's obligations stop being distinguishable from the caller's and the caller
carries them past the call — the reading PL-1 rejected. The decoration pass now
takes a delivery site rather than a step id, because a call site has neither a
bound step nor a binding, and states on the block what extent the obligations
bind over: scoped to the named call, ending with it, joining no container tree.
A bound step carries no such line, its scope being the step. The items are
identical either way, which is what makes parity the delivered property.

Delivery keys on the operation rather than on the spelling that reached it.
`canonicalTechniqueId` names a technique by its home workflow and its path in
that tree, so an unqualified `group::op` through the meta fallback, a
`workflow::group::op`, and a folded call site's relative link are one ledger key
and one body per agent context. Call-site annotations key apart from the body,
so a body shared by two call sites still collapses while each site keeps its own
scope.

Measured on the heaviest closure in the corpus,
`meta::workflow-engine::workflow-orchestrator` at 15 members, with each basis
stated: operation bodies alone 37,668 characters (5.89% of the 640,000-character
budget); the full projection including rules and inherited blocks 146,769
(22.93%); the rules text within that 97,080 summed per member, against 28,801
carried once and 20,348 for the rules the caller does not already hold. The
note-versus-items split is what turns the first figure into the second: one
container's rules are the same bytes for every member inheriting them, and only
the call-scoped note differs.

The fan-out measure reads both halves of the partition, since reading one would
report a fall in fan-out where the delivered set is unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…doors

The activity and orchestrator doors carry the bodies of the operations their
techniques call inline. The operations bundle is the channel they are charged to:
at the orchestrator door it takes no budget parameter and cannot drop content, so
a folded body cannot fail to arrive where the core-operations entry compensating
for it could not; at the activity door the same channel's serialised size seeds
the eager step-technique counter, so a folded body spends budget a step technique
would otherwise have had.

The closure is computed over a door's whole delivered set rather than one
reference at a time. Every delivered body seeds the visited set before the walk
starts, so an operation the door already carries is not folded in beside a copy of
itself, and an operation two delivered techniques both reach is folded in once.
Bodies key by operation and annotations key by call site, so a body shared by two
call sites still collapses while every edge stays visible in
`folded_call_sites` — a revisit is a real call, and omitting it would hide a call
the agent has to make.

Collapse scope at the orchestrator door is documented rather than claimed.
`get_workflow` takes no agent identity, so its ledger scope is the session's own
agent, which several contexts can hold at once; per-technique collapse there is
response-local and the cross-call collapse stays whole-bundle. SC-7a's
per-technique collapse against a step-bound delivery holds at the activity door,
which is dispatched under a worker identity.

Measured. The orchestrator list reproduces at exactly twenty entries, seventeen
of them operations and three group-prefix rule references. Its folded closure is
4 bodies over 19 further edges that reach bodies the list already delivers, and
0 unresolved. At the meta door that block is 33,055 characters, taking the
operations bundle from 80,259 to 113,314 and bootstrap-time fixed content past
its stated budget, which moves to 145,000 with the reason recorded at the
threshold. The four bodies are `continue-batch`, `finalize-activity`,
`variable-binding` and `version-control::push-branch` — none of them named in the
twenty-entry list, so each was an operation an orchestrator was told to apply and
had no way to read. The activity door's core worker list folds 2 bodies at 17,313
characters.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…es ride

The core-set table carries the orchestrator list at its twenty entries, naming
the six the table omitted — `sync-progress-status`, `resolve-harness-operation`,
and the four harness files — and says that three of the twenty are group-prefix
rule references expanding to rules rather than to bodies, so seventeen name
operations.

A new section states which door delivers folded bodies, the counter each is
charged to, and how far collapse reaches at each: the orchestrator door
unbudgeted and collapsing only as a whole bundle for want of an agent identity,
the activity door charged to the counter its own eager tally opens and collapsing
per technique in the dispatched worker's scope, and the step-bound door
delivering none. It also states the delivery key: an operation's canonical
identity rather than the spelling that reached it, which is what lets a folded
callee collapse against a step-bound delivery of the same operation.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
An entry existed in these lists whenever a technique was named inside another
technique's protocol, because a reference found during resolution reached the
agent as prose it could not follow. Both bundle doors now deliver the bodies
those references name, on the same operations-bundle channel the entries ride, so
an entry whose only reason was standing in for an inline reference is redundant
where the closure reaches it.

Retirement is per door and verified for simultaneous removal rather than one
entry at a time, which would not be sound: the reduced list's closure was
measured and every retired entry still arrives as a folded body, with no body
lost against the full list. The orchestrator list goes from 20 entries to 12,
still delivering 21 bodies; the worker list from 8 to 5, still delivering 6.
Bootstrap-time fixed content falls from 137,916 characters to 133,440 across the
same delivered set, the saving being the duplicate rule entries that fewer
touched techniques no longer produce.

Residue is reported rather than removed optimistically, each with the reason
folding does not address it. An entry nothing else reaches is where a walk starts
rather than somewhere it arrives. A rule reference names no body, so folded
delivery never stands in for one. And the four harness adapters stay because
`resolve-harness-operation` picks its callee out of the kind-to-file map rather
than through a link: the callee is named by a value, no link-keyed traversal
reaches it, and this is the one residue with a door still owed.

The non-runtime consumer follows the list unchanged: `check-harness-adapter-set`
excludes exactly the three retired `harness-compat` refs from the adapter
derivation already, so it reads the same four slugs and still holds the two
enumerations to agreement.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A callee chosen at run time is counted two ways, and both figures are published
with their bases. The link-keyed basis counts a call site whose link destination
carries a `{token}`: the corpus has none, and that zero is a fact about
templated destinations rather than a claim that no callee is ever chosen at run
time. The activity-layer basis counts the shapes that occur — 8 callees supplied
as a step-binding input value, and 4 drawn from the harness kind-to-file table,
12 together at corpus pin `12400e85`.

Neither figure stands in for the other, and no widening reaches the gap between
them. A step binding makes its choice in the activity YAML, so the calling
technique's prose holds nothing to resolve; the harness table's readers apply
whatever the kind resolves to, so the callee is a table row. Both are addressed
in a different plane from a markdown link, so no verb-list or link-grammar width
would ever admit them. Publishing the two side by side is the discipline the
coverage percentage already follows: a reproducible zero over an empty
population must not read as coverage of a population that is not empty.

The table is read once. `check-harness-adapter-set` exports its map-row parse and
the census consumes it, so the two enumerations of one table cannot drift — which
is the property that guard exists to hold.

Leaving the enumeration vacuous carried a concrete residue rather than a
theoretical one: the four harness adapter entries stay listed in the core
orchestrator set for as long as nothing can reach them, which would preserve in
miniature the papering-over that retiring 20 entries to 12 and 8 to 5 has just
removed at scale.

The second half of the criterion is untouched. Closure over the enumerable
harness set is checked by `check-harness-adapter-set`, and it passes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…lared window

get_technique carries the bodies of the operations its technique calls inline, under
folded_techniques beside the requested technique. The response is one envelope on
every path: the requested technique under technique:, the closure beside it under
the same folded_* keys the two bundle doors already emit, so one reading of a folded
block serves all three doors rather than each door teaching its own shape.

This is the one door that takes a budget, and the asymmetry is the design. The two
bundle doors charge folded bodies to an operations bundle that cannot drop content,
which is what makes retiring the compensating core-operations entries like-for-like;
a budget on either of them would take that property away, so buildFoldedBundle
leaves its bound unbounded by default and only this door passes one. The step door
answers one fetch at a time, so it sizes the attachment against the window the
caller declares.

The budget names what it withholds. Declaring context_tokens is what asks for the
bodies; without it the response carries the requested technique and states that it
carries nothing else, so the absence is read rather than inferred. The requested
technique is delivered either way and seeds the tally, so the budget describes the
whole response. Bodies attach until the remainder will not hold the next one, and
each one left unsent is listed by identity and composed size under folded_deferred.
The walk does not stop at the first body that will not fit: closure members carry no
execution order between them, so a large body does not deny the small ones behind
it. A budget that dropped content silently is the failure this package exists to
remove, which is why this one reports instead.

Measured at corpus pin 12400e8. 97 techniques have a non-empty step-door closure,
1,451,224 characters of attachment across them, on the basis of summed
delivery-event chars — the composed body of each delivered member, excluding the
call-site rows and note the serialised block adds. The heaviest is
workflow-engine::workflow-orchestrator: 15 bodies over 35 edges, 157,140 characters
against its own 13,864. That closure does not fit a 50,000-token window, which
leaves 146,136 characters after the requested body — so the deferral report answers
a live case rather than a hypothetical one, and fits from 100,000 tokens up.

The heaviest figure is cross-derived rather than measured twice. The walk's own
accounting gives 15 delivered bodies and 35 edges; an independent grammar scan of
the same file set gives 35 resolvable call sites and 15 distinct call targets, with
0 unresolved on both readings. Two readings of one closure, one counting what the
walk did and one counting what the text says.

The door-and-counter table carries the step door's row, and the two sentences this
change falsified — the API table's parameter and response columns, and the protocol
specification's account of what get_technique delivers — are corrected with it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ivers

The stealth-isolation guard scans the delivered closure of every reachable step
rather than that step's bound technique alone. A technique whose protocol calls
another technique has the callee's body delivered alongside it, so an invocation one
call deep is as available to the agent as one written inline — and a scan stopping at
the bound technique reports clean over it. Each finding names the member the text sits
in and the step that reaches it, so a hit one call deep is as actionable as a direct
one. Closures resolve once per operation rather than once per step, which is what
keeps the cost a rounding error: 69 distinct closures serve the whole scan.

The scope is discovered from the corpus instead of naming one workflow. Every
workflow seeding stealth_mode: true is checked, so a stealth workflow joining the
corpus is covered without an edit here — a hard-coded id is how a second one goes
unchecked with the guard still reporting clean. Nothing in scope exits unmeasured
rather than clean.

Scanning every workflow was measured and rejected rather than assumed unworkable.
The public-write catalog is a stealth catalog: updating a pull request through
gh api -X PATCH is this repo's required REST path, correct wherever disclosure is the
point and a leak only under a no-disclosure contract. Run across all 16 workflows it
reports 11 findings, every one of them a sanctioned operation. The scope that makes
the catalog mean what it says is the stealth set.

The widening is cross-derived rather than asserted. In remediate-vuln, 29 techniques
are reachable only by inline call and never as a bound step, so the old scan could not
read them at all. Injecting a gh issue comment into one of those 29 —
gitnexus-operations::impact, reached from five different steps — the pre-change guard
reports OK and this one reports 5 findings naming each call path. Same corpus, two
implementations, opposite verdicts, and the difference is exactly the closure.

The delivery benchmark measures a named scenario. A scenario is a delivery shape
rather than a bundle of flags, which matters because a comparison is only valid
between runs of the same shape: the gate already refuses to fire across context modes
and workflows, and the scenario is what lets a third dimension join that check instead
of being lost in an unrecorded flag. A fixture recorded before scenarios reads as the
solo shape, which is what it is. Every knob a scenario sets stays overridable.

The referenced-technique scenario has every step-bound fetch declare a window, so the
step door attaches the closure its technique calls inline. The two bundle doors fold
unconditionally and are already in every run; this door folds only when asked, so
reaching it is what a scenario is for.

Measured against solo on the same corpus. Total delivery goes from 1,446,744 to
1,524,585 characters and the step door from 160,416 to 238,257 — 77,841 more, 48.5%
of that door and 5.38% of the run. 24 folded bodies over 11 of 23 step-bound fetches,
0 deferred at a 200,000-token window. The other three doors are byte-identical across
the two scenarios, which is what attributes the whole delta to the step door rather
than leaving it inferred.

That 0 deferrals agrees with a reading taken the other way round: a static sweep of
the corpus puts the heaviest step-door closure at 157,140 characters and finds it fits
from 100,000 tokens up, so a 200,000-token walk should defer nothing, and the walk
deferred nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@m2ux m2ux changed the title Inline technique handling: calls between techniques resolve, get checked, and arrive as steps Inline technique calls: composition inside a group, an activity step across one Aug 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant