Summary
reconcile_input_set (src/cli/cmd_build.cpp) routes a removed input back to its consuming command by walking the old index's edges from that file. It follows edge types that reach a command in one hop (Normal, OrderOnly).
Group membership needs two hops and is therefore not routed:
LinkType::Group runs file → group (src/graph/builder.cpp:2117)
- the group → command hop is a separate
LinkType::OrderOnly edge (src/graph/builder.cpp:2523, via the deferred group edges)
The original code admitted Group into the filter and then called idx.find_command_by_id(edge->to), which returns nullptr for any id that is not a command (src/index/entry.cpp:139-142). Every Group edge was therefore discarded — the arm was dead code that read as coverage. It has been removed and this gap documented in its place rather than left as a branch implying a guarantee it never provided.
A changed group member routes fine: collect_affected_commands does a multi-hop BFS over data_flow | order_only on the live graph (src/graph/dag.cpp:1158-1175). Only the removed case, which must go through the index, is affected.
Status: gap is real, but I could not construct a failing case
Deleting a group member's rule and its file together does not reproduce it — the consumer re-runs by another route:
mkdir -p g/{a,b} && cd g && touch Tupfile.ini
printf ': one.txt |> cp %%f %%o |> one.o <objs>\n: two.txt |> cp %%f %%o |> two.o <objs>\n' > a/Tupfile
printf 'x\n' > a/one.txt; printf 'x\n' > a/two.txt
printf ': | ../a/<objs> |> ls ../a > %%o |> listing.txt\n' > b/Tupfile
putup configure && putup # listing.txt: one.o one.txt two.o two.txt
printf ': one.txt |> cp %%f %%o |> one.o <objs>\n' > a/Tupfile # drop the two.o rule
rm -f a/two.o
putup # listing.txt correctly loses two.o
For the second hop to be load-bearing the member must be absent from the current graph and changed on disk and still carry a Group edge in the old index. Every construction I tried satisfied at most two of the three. So this is filed as a known structural gap rather than a reproducible bug — if you find the triggering shape, it belongs here.
Related
Adjacent to the same asymmetry as #166: change detection walks the old index while change routing walks the current graph, so anything reachable only through index topology needs an explicit walk, and that walk is currently single-hop.
🤖 Generated with Claude Code
https://claude.ai/code/session_01HDzr9JV1ofTZKCL1H2qDPg
Summary
reconcile_input_set(src/cli/cmd_build.cpp) routes a removed input back to its consuming command by walking the old index's edges from that file. It follows edge types that reach a command in one hop (Normal,OrderOnly).Group membership needs two hops and is therefore not routed:
LinkType::Groupruns file → group (src/graph/builder.cpp:2117)LinkType::OrderOnlyedge (src/graph/builder.cpp:2523, via the deferred group edges)The original code admitted
Groupinto the filter and then calledidx.find_command_by_id(edge->to), which returnsnullptrfor any id that is not a command (src/index/entry.cpp:139-142). EveryGroupedge was therefore discarded — the arm was dead code that read as coverage. It has been removed and this gap documented in its place rather than left as a branch implying a guarantee it never provided.A changed group member routes fine:
collect_affected_commandsdoes a multi-hop BFS overdata_flow | order_onlyon the live graph (src/graph/dag.cpp:1158-1175). Only the removed case, which must go through the index, is affected.Status: gap is real, but I could not construct a failing case
Deleting a group member's rule and its file together does not reproduce it — the consumer re-runs by another route:
mkdir -p g/{a,b} && cd g && touch Tupfile.ini printf ': one.txt |> cp %%f %%o |> one.o <objs>\n: two.txt |> cp %%f %%o |> two.o <objs>\n' > a/Tupfile printf 'x\n' > a/one.txt; printf 'x\n' > a/two.txt printf ': | ../a/<objs> |> ls ../a > %%o |> listing.txt\n' > b/Tupfile putup configure && putup # listing.txt: one.o one.txt two.o two.txt printf ': one.txt |> cp %%f %%o |> one.o <objs>\n' > a/Tupfile # drop the two.o rule rm -f a/two.o putup # listing.txt correctly loses two.oFor the second hop to be load-bearing the member must be absent from the current graph and changed on disk and still carry a
Groupedge in the old index. Every construction I tried satisfied at most two of the three. So this is filed as a known structural gap rather than a reproducible bug — if you find the triggering shape, it belongs here.Related
Adjacent to the same asymmetry as #166: change detection walks the old index while change routing walks the current graph, so anything reachable only through index topology needs an explicit walk, and that walk is currently single-hop.
🤖 Generated with Claude Code
https://claude.ai/code/session_01HDzr9JV1ofTZKCL1H2qDPg