instar 1.3.1069 → 1.3.1070
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/ConvergenceChecker.js +1 -1
- package/dist/core/ConvergenceChecker.js.map +1 -1
- package/dist/core/PostUpdateMigrator.js +1 -1
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/data/standards-registry.meta.json +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +20 -20
- package/src/data/standards-registry.meta.json +1 -1
- package/src/templates/scripts/convergence-check.sh +1 -1
- package/upgrades/1.3.1070.md +61 -0
- package/upgrades/side-effects/convergence-check-settling-word-boundary.md +103 -0
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-07-
|
|
5
|
-
"instarVersion": "1.3.
|
|
4
|
+
"generatedAt": "2026-07-30T04:21:25.716Z",
|
|
5
|
+
"instarVersion": "1.3.1070",
|
|
6
6
|
"entryCount": 202,
|
|
7
7
|
"entries": {
|
|
8
8
|
"hook:session-start": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"domain": "identity",
|
|
12
12
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
13
13
|
"installedPath": ".instar/hooks/instar/session-start.sh",
|
|
14
|
-
"contentHash": "
|
|
14
|
+
"contentHash": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
15
15
|
"since": "2025-01-01"
|
|
16
16
|
},
|
|
17
17
|
"hook:dangerous-command-guard": {
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"domain": "safety",
|
|
21
21
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
22
22
|
"installedPath": ".instar/hooks/instar/dangerous-command-guard.sh",
|
|
23
|
-
"contentHash": "
|
|
23
|
+
"contentHash": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
24
24
|
"since": "2025-01-01"
|
|
25
25
|
},
|
|
26
26
|
"hook:grounding-before-messaging": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"domain": "safety",
|
|
30
30
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
31
31
|
"installedPath": ".instar/hooks/instar/grounding-before-messaging.sh",
|
|
32
|
-
"contentHash": "
|
|
32
|
+
"contentHash": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
33
33
|
"since": "2025-01-01"
|
|
34
34
|
},
|
|
35
35
|
"hook:compaction-recovery": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"domain": "identity",
|
|
39
39
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
40
40
|
"installedPath": ".instar/hooks/instar/compaction-recovery.sh",
|
|
41
|
-
"contentHash": "
|
|
41
|
+
"contentHash": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
42
42
|
"since": "2025-01-01"
|
|
43
43
|
},
|
|
44
44
|
"hook:external-operation-gate": {
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"domain": "safety",
|
|
48
48
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
49
49
|
"installedPath": ".instar/hooks/instar/external-operation-gate.js",
|
|
50
|
-
"contentHash": "
|
|
50
|
+
"contentHash": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
51
51
|
"since": "2025-01-01"
|
|
52
52
|
},
|
|
53
53
|
"hook:deferral-detector": {
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"domain": "safety",
|
|
57
57
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
58
58
|
"installedPath": ".instar/hooks/instar/deferral-detector.js",
|
|
59
|
-
"contentHash": "
|
|
59
|
+
"contentHash": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
60
60
|
"since": "2025-01-01"
|
|
61
61
|
},
|
|
62
62
|
"hook:self-stop-guard": {
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"domain": "coherence",
|
|
66
66
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
67
67
|
"installedPath": ".instar/hooks/instar/self-stop-guard.js",
|
|
68
|
-
"contentHash": "
|
|
68
|
+
"contentHash": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
69
69
|
"since": "2025-01-01"
|
|
70
70
|
},
|
|
71
71
|
"hook:post-action-reflection": {
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"domain": "evolution",
|
|
75
75
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
76
76
|
"installedPath": ".instar/hooks/instar/post-action-reflection.js",
|
|
77
|
-
"contentHash": "
|
|
77
|
+
"contentHash": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
78
78
|
"since": "2025-01-01"
|
|
79
79
|
},
|
|
80
80
|
"hook:external-communication-guard": {
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"domain": "safety",
|
|
84
84
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
85
85
|
"installedPath": ".instar/hooks/instar/external-communication-guard.js",
|
|
86
|
-
"contentHash": "
|
|
86
|
+
"contentHash": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
87
87
|
"since": "2025-01-01"
|
|
88
88
|
},
|
|
89
89
|
"hook:scope-coherence-collector": {
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"domain": "coherence",
|
|
93
93
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
94
94
|
"installedPath": ".instar/hooks/instar/scope-coherence-collector.js",
|
|
95
|
-
"contentHash": "
|
|
95
|
+
"contentHash": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
96
96
|
"since": "2025-01-01"
|
|
97
97
|
},
|
|
98
98
|
"hook:scope-coherence-checkpoint": {
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
"domain": "coherence",
|
|
102
102
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
103
103
|
"installedPath": ".instar/hooks/instar/scope-coherence-checkpoint.js",
|
|
104
|
-
"contentHash": "
|
|
104
|
+
"contentHash": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
105
105
|
"since": "2025-01-01"
|
|
106
106
|
},
|
|
107
107
|
"hook:free-text-guard": {
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
"domain": "safety",
|
|
111
111
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
112
112
|
"installedPath": ".instar/hooks/instar/free-text-guard.sh",
|
|
113
|
-
"contentHash": "
|
|
113
|
+
"contentHash": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
114
114
|
"since": "2025-01-01"
|
|
115
115
|
},
|
|
116
116
|
"hook:claim-intercept": {
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
"domain": "coherence",
|
|
120
120
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
121
121
|
"installedPath": ".instar/hooks/instar/claim-intercept.js",
|
|
122
|
-
"contentHash": "
|
|
122
|
+
"contentHash": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
123
123
|
"since": "2025-01-01"
|
|
124
124
|
},
|
|
125
125
|
"hook:claim-intercept-response": {
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
"domain": "coherence",
|
|
129
129
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
130
130
|
"installedPath": ".instar/hooks/instar/claim-intercept-response.js",
|
|
131
|
-
"contentHash": "
|
|
131
|
+
"contentHash": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
132
132
|
"since": "2025-01-01"
|
|
133
133
|
},
|
|
134
134
|
"hook:stop-gate-router": {
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
"domain": "safety",
|
|
138
138
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
139
139
|
"installedPath": ".instar/hooks/instar/stop-gate-router.js",
|
|
140
|
-
"contentHash": "
|
|
140
|
+
"contentHash": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
141
141
|
"since": "2025-01-01"
|
|
142
142
|
},
|
|
143
143
|
"hook:auto-approve-permissions": {
|
|
@@ -146,7 +146,7 @@
|
|
|
146
146
|
"domain": "safety",
|
|
147
147
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
148
148
|
"installedPath": ".instar/hooks/instar/auto-approve-permissions.js",
|
|
149
|
-
"contentHash": "
|
|
149
|
+
"contentHash": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
150
150
|
"since": "2025-01-01"
|
|
151
151
|
},
|
|
152
152
|
"job:health-check": {
|
|
@@ -1138,7 +1138,7 @@
|
|
|
1138
1138
|
"type": "template",
|
|
1139
1139
|
"domain": "operations",
|
|
1140
1140
|
"sourcePath": "src/templates/scripts/convergence-check.sh",
|
|
1141
|
-
"contentHash": "
|
|
1141
|
+
"contentHash": "d63901cf7110861b2a65a40b5c876902bd972b53e6a4c7ca4bf55db76e8e640a",
|
|
1142
1142
|
"since": "2025-01-01"
|
|
1143
1143
|
},
|
|
1144
1144
|
"template:emit-session-clock.sh": {
|
|
@@ -1562,7 +1562,7 @@
|
|
|
1562
1562
|
"type": "subsystem",
|
|
1563
1563
|
"domain": "updates",
|
|
1564
1564
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
1565
|
-
"contentHash": "
|
|
1565
|
+
"contentHash": "f3d204de3351e43ab52f77e47a0bf86fb4c71d0c3bf968f48c40649493c8f7cd",
|
|
1566
1566
|
"since": "2025-01-01"
|
|
1567
1567
|
},
|
|
1568
1568
|
"subsystem:scheduler": {
|
|
@@ -39,7 +39,7 @@ if echo "$CONTENT" | grep -qiE "(^|[^a-zA-Z])i.ll (make sure|ensure|guarantee|al
|
|
|
39
39
|
fi
|
|
40
40
|
|
|
41
41
|
# 3. SETTLING — Accepting empty results without digging deeper
|
|
42
|
-
if echo "$CONTENT" | grep -qiE "(no (data|results|information) (available|found|exists)|nothing (to report|happened|was found)|there (is|are) no|could(n.t| not) find (any|the)|appears to be empty|no (relevant|matching|applicable))"; then
|
|
42
|
+
if echo "$CONTENT" | grep -qiE "(no (data|results|information) (available|found|exists)|nothing (to report|happened|was found)|there (is|are) no([^a-zA-Z]|$)|could(n.t| not) find (any|the)|appears to be empty|no (relevant|matching|applicable))"; then
|
|
43
43
|
ISSUES+=("SETTLING: You're reporting nothing found. Did you check multiple sources? Could the data source be stale or the search terms wrong? Empty results deserve investigation, not acceptance.")
|
|
44
44
|
ISSUE_COUNT=$((ISSUE_COUNT + 1))
|
|
45
45
|
fi
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
The pre-messaging quality gate (`convergence-check`) has a **settling** rule that
|
|
9
|
+
catches an agent reporting an empty result without digging — "there is no data",
|
|
10
|
+
"nothing was found". It searched for the phrase `there (is|are) no` with no trailing
|
|
11
|
+
word boundary, so it also matched inside longer words that merely begin with "no":
|
|
12
|
+
**nothing**, **none**, **nobody**.
|
|
13
|
+
|
|
14
|
+
The result was that ordinary English got blocked. "There is nothing pathological
|
|
15
|
+
required" was treated identically to "There is no data available."
|
|
16
|
+
|
|
17
|
+
The rule now requires `no` to be a whole word, using the same guard idiom the adjacent
|
|
18
|
+
commitment rule already applies to `promise` (`([^a-zA-Z]|$)`).
|
|
19
|
+
|
|
20
|
+
**The same regex lived in three places** — the shell template, its TypeScript port
|
|
21
|
+
(`ConvergenceChecker`), and an inline fallback in `PostUpdateMigrator` used when the
|
|
22
|
+
template cannot be loaded. All three are corrected, and a new drift guard fails the
|
|
23
|
+
build if they ever stop matching each other. Fixing only the template would have left
|
|
24
|
+
the port broken and shipped the bug to any agent whose template load failed.
|
|
25
|
+
|
|
26
|
+
The genuine catch is preserved by a **different** branch of the same rule: "there is
|
|
27
|
+
nothing to report" still blocks, via `nothing (to report|happened|was found)`. That is
|
|
28
|
+
asserted by an explicit test, because it was the main risk of narrowing the pattern.
|
|
29
|
+
|
|
30
|
+
## What to Tell Your User
|
|
31
|
+
|
|
32
|
+
Nothing is required of you, and nothing changes in how you talk to your agent.
|
|
33
|
+
|
|
34
|
+
Your agent runs a quality check on its own messages before sending them, to catch
|
|
35
|
+
habits like giving up after one empty search. That check was mis-firing on the word
|
|
36
|
+
"nothing" — so an agent writing a perfectly good sentence like "there is nothing
|
|
37
|
+
unusual here" could be stopped and made to rewrite it for no reason.
|
|
38
|
+
|
|
39
|
+
That mis-fire is fixed. The check still catches the real thing it was built for; it
|
|
40
|
+
just no longer trips over three common words. You may notice slightly fewer moments
|
|
41
|
+
where your agent pauses and rephrases itself for no visible reason.
|
|
42
|
+
|
|
43
|
+
## Summary of New Capabilities
|
|
44
|
+
|
|
45
|
+
None — this is a correctness fix to an existing check. No new endpoints, config keys,
|
|
46
|
+
commands, or agent-facing surfaces. The only addition is an internal drift guard that
|
|
47
|
+
keeps the three copies of the rule in sync.
|
|
48
|
+
|
|
49
|
+
## Evidence
|
|
50
|
+
|
|
51
|
+
- 22 new unit tests. Run against the **unfixed** source first: **12 failed / 10 passed**
|
|
52
|
+
there. So 12 assertions genuinely discriminate (6 false-positive cases across the
|
|
53
|
+
shell and TypeScript implementations, plus 6 drift-guard assertions); the other 10
|
|
54
|
+
pass either way and are labelled `CONTROL` in the test file rather than counted as
|
|
55
|
+
evidence.
|
|
56
|
+
- Reproduced from a real incident: a second session of this agent, running on another
|
|
57
|
+
machine, had a correct message blocked twice on "there is nothing pathological
|
|
58
|
+
required" and reported it instead of rewording until something got through.
|
|
59
|
+
- Migration parity verified rather than assumed: `PostUpdateMigrator` writes
|
|
60
|
+
`scripts/convergence-check.sh` unconditionally on every migration run, and the
|
|
61
|
+
template was confirmed byte-identical to the installed copy before editing.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Side-effects review — SETTLING rule word boundary
|
|
2
|
+
|
|
3
|
+
**Change.** The pre-messaging quality gate's SETTLING rule contained the fragment
|
|
4
|
+
`there (is|are) no` with no trailing boundary. It matched inside longer words that
|
|
5
|
+
merely begin with "no" — `nothing`, `none`, `nobody` — so ordinary descriptive
|
|
6
|
+
English was blocked as if the agent were settling for an empty search result. The
|
|
7
|
+
fragment is now `there (is|are) no([^a-zA-Z]|$)`, using the same guard idiom the
|
|
8
|
+
adjacent commitment rule already applied to `promise`.
|
|
9
|
+
|
|
10
|
+
**How it was found.** A second session of this agent, running on the Laptop, had a
|
|
11
|
+
correct message blocked twice on the phrase "there is nothing pathological
|
|
12
|
+
required" and reported it rather than working around it.
|
|
13
|
+
|
|
14
|
+
**The defect was larger than the one character.** The identical regex lives in
|
|
15
|
+
THREE places: the shell template, its TypeScript port (`ConvergenceChecker.ts`),
|
|
16
|
+
and an inline fallback in `PostUpdateMigrator` used when the template cannot be
|
|
17
|
+
loaded. All three are fixed, and a drift guard now asserts they stay identical.
|
|
18
|
+
Fixing only the template would have left the port broken and shipped the bug to
|
|
19
|
+
any agent whose template load failed.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 1. Over-block — what legitimate inputs does this reject that it shouldn't?
|
|
24
|
+
|
|
25
|
+
This change **removes** over-blocking; it adds none. Three realistic sentences that
|
|
26
|
+
were blocked now pass, each verified against the unfixed source first:
|
|
27
|
+
"There is nothing pathological required." / "I checked and there are none of those
|
|
28
|
+
left." / "There is nobody else on that machine."
|
|
29
|
+
|
|
30
|
+
## 2. Under-block — what failure modes does this still miss?
|
|
31
|
+
|
|
32
|
+
The rule now misses `there is no<word>` constructions where the longer word really
|
|
33
|
+
is a settling claim. I could not construct a realistic one — "nothing", "none" and
|
|
34
|
+
"nobody" are the only common continuations, and the genuinely-settling forms among
|
|
35
|
+
them ("there is nothing to report") are caught by a **different** alternative in the
|
|
36
|
+
same rule, `nothing (to report|happened|was found)`. That is asserted explicitly by
|
|
37
|
+
test rather than assumed, because it is the whole reason this narrowing is safe.
|
|
38
|
+
|
|
39
|
+
More broadly, the SETTLING rule remains a heuristic keyword matcher and always was.
|
|
40
|
+
This change does not make it smarter, only less wrong on one fragment.
|
|
41
|
+
|
|
42
|
+
## 3. Level-of-abstraction fit
|
|
43
|
+
|
|
44
|
+
Correct layer. The bug is in the pattern itself, not in the hook that invokes it or
|
|
45
|
+
the gate that consumes the verdict. No caller changes. The deeper question — whether
|
|
46
|
+
a keyword matcher should hold blocking authority at all — is a real one but is out
|
|
47
|
+
of scope here and is not made worse by this change.
|
|
48
|
+
|
|
49
|
+
## 4. Signal vs authority compliance
|
|
50
|
+
|
|
51
|
+
This gate **does** hold blocking authority with brittle logic, which is in tension
|
|
52
|
+
with `docs/signal-vs-authority.md`. That tension pre-exists this change and is
|
|
53
|
+
unchanged by it: the fix strictly narrows a false-positive branch, so it moves the
|
|
54
|
+
brittle authority in the safer direction (fewer wrong blocks) without expanding what
|
|
55
|
+
the check can block. No new authority is introduced, and no rule gains reach.
|
|
56
|
+
|
|
57
|
+
## 5. Interactions
|
|
58
|
+
|
|
59
|
+
None found. The fragment is one alternative inside one alternation in one rule. The
|
|
60
|
+
other six rule categories are untouched. The one interaction that matters is
|
|
61
|
+
intra-rule and is deliberate: `nothing (to report|...)` still catches the true
|
|
62
|
+
positive the narrowed fragment gives up, so the two alternatives now divide the work
|
|
63
|
+
cleanly instead of overlapping.
|
|
64
|
+
|
|
65
|
+
## 6. External surfaces
|
|
66
|
+
|
|
67
|
+
Agent-facing only. No end user sees this gate. Agents will see strictly fewer
|
|
68
|
+
spurious "MESSAGE BLOCKED" refusals on outbound messaging. No API, config key, or
|
|
69
|
+
schema changes. No timing or conversation-state dependence.
|
|
70
|
+
|
|
71
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
72
|
+
|
|
73
|
+
**Machine-local by design, and correctly so** — no `machine-local-justification`
|
|
74
|
+
marker is required because this introduces no new state or feature surface. The
|
|
75
|
+
script is a per-machine installed file, already written to every machine by the
|
|
76
|
+
existing always-overwrite migration path
|
|
77
|
+
(`PostUpdateMigrator` writes `scripts/convergence-check.sh` unconditionally). Each
|
|
78
|
+
machine therefore converges on the fixed copy independently at its next update;
|
|
79
|
+
there is nothing to replicate, merge, or proxy. Migration parity was **verified, not
|
|
80
|
+
assumed**: the write at `PostUpdateMigrator.ts:9943` is unconditional, and the
|
|
81
|
+
template and the installed copy were confirmed byte-identical before editing.
|
|
82
|
+
|
|
83
|
+
Note the asymmetry this creates in the interim: the Laptop, which reported the bug,
|
|
84
|
+
keeps the broken copy until it updates. That is the normal update lag, not a
|
|
85
|
+
coherence defect.
|
|
86
|
+
|
|
87
|
+
## 8. Rollback cost
|
|
88
|
+
|
|
89
|
+
Trivial. Revert one commit; the migration overwrites the script back to the previous
|
|
90
|
+
content on the next update. No data migration, no state repair, no agent-side
|
|
91
|
+
cleanup. The change is additive to a regex and carries no persisted footprint.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Testing
|
|
96
|
+
|
|
97
|
+
22 unit tests. Run against the **unfixed** source first: **12 failed / 10 passed**
|
|
98
|
+
there, so 12 assertions genuinely discriminate (6 false-positive cases + 6 drift-guard
|
|
99
|
+
assertions) and the other 10 are labelled CONTROL in the file because they pass either
|
|
100
|
+
way and are not evidence for this change.
|
|
101
|
+
|
|
102
|
+
The drift guard is the structural half: without it, the next person fixes one copy of
|
|
103
|
+
three and the bug survives in the other two exactly as it did here.
|