Skip to content

Commit c669e6d

Browse files
authored
docs(adr): ADR-179, a dropped forced source is recorded on the run (#828)
The decision behind #809, before any code. No behaviour changes here. **The problem:** a run queued with a forced snippet or skill loses it if someone switches that record off before the run starts. The run proceeds without it and says nothing. The only way to notice is to compare the queued request against the transcript. ADR-175 made this uniform rather than lopsided — before it, a snippet disappeared and a skill survived — which is what turns it into one question with one answer. ## Three decisions **Recorded on the run, not only logged.** A log answers "did it happen", which serves support. The operator asks "why did this run differ from the one I queued", and that is asked at the run. It travels on `RunAugmentation` from the rehydration that drops it and is written through `RunTrace` — ADR-151's channel, whose steps `Backend/AgentRun/Show.html` already renders. No new surface is invented. **The run is not refused.** Refusing a queued run is defensible on cost — nothing has been sent, so it costs only a message. Rejected on *who pays*: switching off a snippet is routine operator maintenance, and making it fail other people's queued work turns a safe action into one nobody dares take. A run that proceeds and says so is recoverable; a refused run is somebody else's incident. **"Deleted" and "switched off" read differently.** Both resolve to nothing today and a single "dropped" would flatten them. They are different actions with different remedies — one is reversible, the other is not — and a reader who cannot tell them apart has to go looking. ## Deliberately out - **The resume path.** ADR-166 and ADR-175 keep a deactivated source resolving there on purpose, because its text is already in the transcript. Nothing is dropped, so a report would imply otherwise. - **Notifications.** The record is readable at the run; it does not push. Whether a dropped source deserves a notification is the same open question as every other pending decision here, and answering it for one case would settle it for all of them by accident. ## One correction I made while writing this I had told the user the anchor was step metadata, citing `droppedTurns` in `ToolLoopService`. That line is a **log** call, not a step. Its own comment names the real one — the context step ADR-151 records through `RunTrace`. The decision above uses that channel; the sentence I said out loud first was wrong. ## Why this is its own PR The implementation is roughly the size of #823: a field on `RunAugmentation`, the codec computing the difference, the trace write, the template, and tests. Splitting the decision out means it can be argued before that exists, and it gives an early checkpoint rather than a long silent stretch — which is the pattern that went wrong earlier today. 68 ADR-suite tests pass; the three repo checks pass. No PHP is touched. Refs #809
2 parents dc1471c + 367ed08 commit c669e6d

3 files changed

Lines changed: 105 additions & 0 deletions

File tree

Documentation/Adr/Adr175ForcedSkillsBindByTheSnippetRule.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ ADR-175: A forced skill binds by the same rule as a forced snippet
99
:Amends: :ref:`ADR-166 <adr-166>` (whose "the skill half was already correct"
1010
held for the resume path it was judging and not for the two composition
1111
paths it did not look at)
12+
:Amended: 2026-08-19 by :ref:`ADR-179 <adr-179>` (it made the two kinds agree on
13+
which sources enter a run and left the silence when one does not)
1214
:Authors: Netresearch DTT GmbH
1315

1416
Context
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
.. include:: /Includes.rst.txt
2+
3+
.. _adr-179:
4+
5+
==============================================================================
6+
ADR-179: A forced source that is dropped is recorded on the run
7+
==============================================================================
8+
9+
:Status: Accepted
10+
:Date: 2026-08-19
11+
:Amends: :ref:`ADR-175 <adr-175>` (which made the two source kinds agree on
12+
WHICH sources enter a run, and left the silence when one does not)
13+
:Authors: Netresearch DTT GmbH
14+
15+
Context
16+
=======
17+
18+
A run queued with a forced snippet or skill resolves that set again when it is
19+
dequeued. :ref:`ADR-175 <adr-175>` settled that both kinds resolve enabled-only
20+
there, because dequeuing is composition and a source an operator switched off
21+
must not enter a prompt being assembled now.
22+
23+
What it did not settle is what the operator is told. Today: nothing. The source
24+
is dropped, the run proceeds without it, and the only way to notice is to
25+
compare the queued request against the transcript. The run does not do what the
26+
person who queued it asked for, and nothing says why.
27+
28+
The silence is now uniform rather than lopsided — before ADR-175 a snippet
29+
disappeared and a skill survived — which is what makes it a question with one
30+
answer instead of two.
31+
32+
Decision
33+
========
34+
35+
**Recorded on the run, not only in a log.** A log line answers "did it happen",
36+
which serves support. The question an operator actually has is "why did this
37+
run behave differently from the one I queued", and that is asked at the run.
38+
The record therefore travels on :php:`RunAugmentation` from the rehydration
39+
that drops it, and is written through :php:`RunTrace` — the same channel
40+
:ref:`ADR-151 <adr-151>` uses for context accounting, so it lands in the step
41+
list ``Backend/AgentRun/Show.html`` already renders. No new surface is invented
42+
for it.
43+
44+
**The run is not refused.** Refusing a queued run whose forced source vanished
45+
is defensible in principle — nothing has been sent, so refusing costs only a
46+
message. It is rejected because of who pays: switching off a snippet is
47+
routine operator maintenance, and making it fail other people's queued work
48+
turns a safe action into one nobody dares take. A run that proceeds without a
49+
source and says so is recoverable; a refused run is somebody else's incident.
50+
51+
**"Deleted" and "switched off" read differently.** Both resolve to nothing
52+
today and a single "dropped" would flatten them. They are different operator
53+
actions with different remedies — a deactivated record can be switched back on,
54+
a deleted one cannot — and a reader who cannot tell them apart has to go
55+
looking. The record names which of the two applied, per uid.
56+
57+
What this does not do
58+
=====================
59+
60+
**It does not change which sources enter a run.** :ref:`ADR-175 <adr-175>`
61+
decides that and is untouched. This decides only what is said about the
62+
difference.
63+
64+
**It does not touch the resume path.** :ref:`ADR-166 <adr-166>` and
65+
:ref:`ADR-175 <adr-175>` keep a deactivated source resolving on a resume, on
66+
purpose — its text is already in the transcript. Nothing is dropped there, so
67+
there is nothing to report, and adding a report would imply otherwise.
68+
69+
**It does not notify anyone.** The record is readable at the run; it does not
70+
push. Whether a dropped source deserves a notification is the same unanswered
71+
question as every other pending decision in this extension, and answering it
72+
here for one case would settle it by accident.
73+
74+
**It does not record a source that was never requested.** The comparison is
75+
against the uids the run was queued with. A caller that sends an unknown uid
76+
gets it reported as unresolved like any other, because from the run's side
77+
those are the same event.
78+
79+
Consequences
80+
============
81+
82+
An operator reading a run sees, next to the sources that were injected, the
83+
ones that were asked for and did not arrive, and which of the two things
84+
happened to each.
85+
86+
:php:`RunAugmentation` gains a field. It is ``@internal``, so no frozen surface
87+
moves.
88+
89+
The playground's synchronous send composes the same way and gains the same
90+
record. The queued path is where the gap between request and start is wide
91+
enough to matter, but a source can be switched off during a playground round
92+
too, and one path reporting while the other stays silent would be the
93+
asymmetry ADR-175 just removed.
94+
95+
Revisit when
96+
============
97+
98+
- Notifications arrive. This record deliberately does not push, and that
99+
becomes a choice worth re-taking rather than an omission.
100+
- A third reason a forced source fails to resolve appears — a permission, a
101+
workspace, a language overlay. The two-way split above would then be
102+
hiding a case rather than distinguishing one.

Documentation/Adr/Index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,3 +529,4 @@ Tools
529529
Adr175ForcedSkillsBindByTheSnippetRule
530530
Adr176PerCallOutcomeSeparateFromApproval
531531
Adr177CallerSourceAttribution
532+
Adr179ADroppedForcedSourceIsRecordedOnTheRun

0 commit comments

Comments
 (0)