Tinderbox Forum

Restricting agent scope to a specific path

I think my indents were wrong, I was posting from my phone and it is no easy feat , spaces are stripped, anything with more than three dots is displayed as 3 dots…

Node D was intended to be a sibling of Note B, then your solution should work (I am saying should because I am still working from the phone as of yesterday and I haven’t tried it yet).


1 Like

Great! I thought that might be the case. So, I hope that helps you once you are back at your Mac.

For later readers, it’s worth noting one thing here. The agent is in-scope of queries based on the agent’s parent. In the above case, the agent is then excluded by the query term matching a specific prototype. However, when using a scenario like this, do take care that the agent isn’t matching itself (or the aliases within it) as it could create a loop and give the app a headache and you the wrong result).

1 Like

Yep it works just fine! And yes I had taken into account recursive-ity of the query when asking.

I have decided to have one TBX file to rule them all on some work matters, but as a result I need to reduce the scope of agents (for the time being) to a specific outline branch. I will experiment a bit more with this approach, if it proves to be more painful than I thought I am going to revert to one file per major area.


1 Like

Thanks. My follow-up warning was less for you that later reader of the thread as people often take ideas for old threads and adapt them (as they are encouraged to do!). It just struck me that a new user might easily overlook the fact the agent was within its own query scope, if using only inside() or descendedFrom() as the query. :slight_smile:

Agents don’t find themselves, so that isn’t something to worry about.

I agree you want to make sure you’re not matching against the aliases within the agent, but I’ve also never run into a situation where I’ve done that (though it is possible).

With this agent query:

descendedFrom(/parent note) & !descendedFrom(/my agent)

You’ll see the agent toggling between showing child note, and then hiding it:



In context it is worth noting that inside() has a subtly different behaviour from descendedFrom(). inside(item) will match if either the original or an alias of a note is a child of (i.e. ‘inside’) item whereas descendedFrom(item) only matches originals descended from item. If you need a child-scoped original-only match you can use descendedFrom() if you know there are only children of the target container (e.g. it is an agent) or consider using using an additional &$IsAlias==false query term with inside().

See the @pat’s post below for a more accurate description.

That is incorrect. descendedFrom also matches aliases.

The subtly different behavior arises from the fact that that when you combine multiple descendedFrom expressions, it does what most people expect – but when you combine multiple inside expression, it does not do what most people expect. (why that’s the case, I’ve read multiple explanations of but still don’t quite understand)

This is precisely why descendedFrom is useful to target the children of an agent – because inside has a pretty big gotcha that prevents it from being useful in that case, but descendedFrom gets the job done.

Here’s a document showing how descendedFrom finds aliases, and comparing the difference between inside and descendedFrom. It also demonstrates an approach for achieving the goal that people are aiming for when they combine multiple inside expressions. inside vs descendedFrom.tbx (81.2 KB)

To achieve a combination of multiple inside expressions, you need to approach it from the other side: examining children of a container:

// this finds notes that are inside both container
collect(children(/container 1), $ID(original)).contains($ID(original))
  & collect(children(/container 2), $ID(original)).contains($ID(original))

// this finds notes that are inside container 1, but not container 2
collect(children(/container 1), $ID(original)).contains($ID(original))
  & !(collect(children(/container 2), $ID(original))).contains($ID(original))

Ah , yes. Note to self that if/when I get some spare time I should re-document this more clearly in aTbRef. I can attest to getting caught out by inside() behaving in a way I didn’t expect.

And I can attest that, thanks to @mwra, aTbRef has saved my bacon by telling me exactly what I needed to know in a pinch, more times than I’ll ever be able to count :tada::tada::tada:


The only reason I can do all the cool stuff I can do with Tinderbox is because of aTbRef!! I constantly have it open, and I pretty much live in Attributes / Action Codes / Export Codes / Designators pages. I’ve barely looked at the rest of it yet – including all the apparently neat things you can do in map view.

Good to hear - because that’s me too! There’s simply too much to remember. The forum is good for kicking things over. This thread, needing to loop on a list reminded my that whereas we used to have to use list.contains() instead of == for Set/List types, we now have the list.each() option to iterate the list and == test each list value. Sure it’s a little bit more code but not too complex once you’ve figured it through the first time.

A note is inside(target) if it’s a child of the target note, or if one of its aliases is a child of the target note.

The second clause lets you agents work together more easily.


is true if a note matches the agent’s query.

Yes, but as far as I can tell, that’s only true for one application of inside().

A note is not inside(target 1) & inside(target 2), even if it is a child of target 1 and one of its aliases is a child of target 2.

That’s the tricky bit.

  1. I made a new document with two containers, c1 and c2.

  2. A made a note A, and an alias of A. I movedA itself into c1, and its alias into C2.

  3. I made an agent with the query: inside(c1) & inside(c2)

  4. The agent finds one note, A, which is inside both c1 and c2.

ah right, the original has to be inside one of them for it to work

  1. I made a new document with two containers, c1 and c2.
  2. I made a note A, and two aliases of A. I moved one alias into c1, and the other alias into C2.
  3. I made an agent with the query: inside(c1) & inside(c2)
  4. The agent finds nothing, even though aliases of A are inside both c1 and c2.

aliases-inside-but-not-found.tbx (55.7 KB)

1 Like


The original semantics held that

A note is inside(target) if it’s a child of the target note, or if one of its aliases is a child of the target note.

More precisely, inside() was true if the note was a child of the target, or if this note, an alias, was a child of the target. This, as you observed, is always false for two aliases, since different notes are this in each case.

Starting with b358, we’ll change this slightly:

A note is inside(target) if it’s a child of the target note, or if any of its aliases is a child of the target note.

Cool! I think this change will make the inverse true as well, correct?

A note is not inside(target) if it is not a child of the target note, and if none of its aliases is a child of the target note.

See this document, where inside(c1) & !inside(c2) finds the note, even though one of its aliases is inside c1.*

alias-inside.tbx (56.2 KB)

* I understand why it does that right now (the original and the second alias are both inside not inside c1), but it still trips me up – and other people

Yes: the revised inside() agrees that no notes are inside(c1) & (!inside(c2))

1 Like

RE descendedFrom() & inside() behaviours it would be much easier if the default was to match only originals in respective scope and use an optional boolean argument to tell the operator to also match where only an alias is in scope (as an original always trumps if both are in scope).

I respect that the original ambiguities go back >15 years to when action query code was a lot less complex (inside() used to be #inside()). Not it is more complex, having clearer demarcation treatment of how aliases matches modify results would IMO help. Also, IIRC the alias-match aspect tended to bite when trying to use a ‘reverse’ containment, i.e. checking not inside/descended from.