Skip to content

Group-member removal is not routed: reconcile_input_set walks one hop, group membership needs two #169

Description

@typeless

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions