patch-cc 0.5.0__tar.gz → 0.5.1__tar.gz
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.
- {patch_cc-0.5.0 → patch_cc-0.5.1}/PKG-INFO +1 -1
- {patch_cc-0.5.0 → patch_cc-0.5.1}/docs/PLAYBOOK.md +98 -36
- {patch_cc-0.5.0 → patch_cc-0.5.1}/docs/corpus.md +6 -4
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/__init__.py +1 -1
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/patches/streaming.py +449 -90
- {patch_cc-0.5.0 → patch_cc-0.5.1}/.github/workflows/ci.yml +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/.github/workflows/release-watch.yml +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/.github/workflows/release.yml +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/.gitignore +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/AGENTS.md +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/CLAUDE.md +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/LICENSE +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/README.md +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/docs/CONDUCT.md +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/docs/INTERNALS.md +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/docs/demo.gif +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/pyproject.toml +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/bun/__init__.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/bun/blob.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/bun/container.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/bun/elf.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/bun/errors.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/bun/macho.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/cache.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/cli.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/codex/__init__.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/codex/gateway.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/codex/models.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/codex/oauth.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/codex/translate.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/doctor.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/js.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/locate.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/menu.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/patcher.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/patches/__init__.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/patches/agents.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/patches/base.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/patches/chrome.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/patches/codex.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/patches/output.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/patches/thinking.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/src/patch_cc/ui.py +0 -0
- {patch_cc-0.5.0 → patch_cc-0.5.1}/uv.lock +0 -0
|
@@ -824,12 +824,15 @@ for you. Each entry: what it changes, the stable anchor, and where it lives.
|
|
|
824
824
|
|
|
825
825
|
- **`live-thinking`** — the fourteen-step patch above.
|
|
826
826
|
|
|
827
|
-
**`prop-threading`**
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
827
|
+
**`prop-threading`** hands the live-thinking state to every conversation
|
|
828
|
+
render — a props bag *handed to a component* (an argument) that carries
|
|
829
|
+
`conversationId` and `messages`. Being an argument is part of the identity:
|
|
830
|
+
a module-level literal, a return-value payload, or a config object may
|
|
831
|
+
legitimately carry the pair, and none of them is a render. On a build whose
|
|
832
|
+
component owns the state it inserts `streamingThinking:<state>,` before the
|
|
833
|
+
bag's `conversationId`; on a store build it reroutes the render through a
|
|
834
|
+
wrapper component of ours that subscribes itself (below). Three things do
|
|
835
|
+
the work, and each replaced something that had broken:
|
|
833
836
|
|
|
834
837
|
- **Identity is membership — of the essential props alone.** The props that
|
|
835
838
|
make a render a conversation render, asked of one object rather than of a
|
|
@@ -868,37 +871,96 @@ for you. Each entry: what it changes, the stable anchor, and where it lives.
|
|
|
868
871
|
*initialised* to is deliberately not asked: `useState(null)` is every
|
|
869
872
|
build's spelling and `useState(void 0)` would be the same state, while the
|
|
870
873
|
setter is the identity that matters.
|
|
871
|
-
- **The state has two homes, and each is
|
|
872
|
-
Through 2.1.235 the scope that hands `onStreamingThinking`
|
|
873
|
-
state itself, and the state is the array pattern binding the
|
|
874
|
-
— `useState`'s pair, which nothing else names
|
|
874
|
+
- **The state has two semantic homes, and each is identified by the only
|
|
875
|
+
name it has.** Through 2.1.235 the scope that hands `onStreamingThinking`
|
|
876
|
+
declared the state itself, and the state is the array pattern binding the
|
|
877
|
+
handed setter — `useState`'s pair, which nothing else names; its value is
|
|
878
|
+
threaded into the bag as it stands. 2.1.236 moved the state into an
|
|
875
879
|
external stream store (`subscribe`/`getSnapshot`/`_publish` — the
|
|
876
|
-
`useSyncExternalStore` shape)
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
(
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
880
|
+
`useSyncExternalStore` shape), and there the question this step used to
|
|
881
|
+
ask — *which idiom holds the state* — is retired, because it broke on
|
|
882
|
+
every answer it ever gave. Each answer was a spelling of React's
|
|
883
|
+
data-access fashion, the busiest surface upstream owns: the handing ("the
|
|
884
|
+
call's only argument is the very expression the setter was read off")
|
|
885
|
+
died on 2.1.246, when the handing moved into the engine
|
|
886
|
+
(`this.stream.setStreamingThinking`) with the state unmoved and
|
|
887
|
+
`onStreamingThinking:` still twice in the bundle; the whole-snapshot
|
|
888
|
+
destructure that replaced it (`{streamingToolUses:…}=useX(<store>)`, the
|
|
889
|
+
pattern naming the store's own field) died on 2.1.247, which reads the
|
|
890
|
+
store through per-field selectors instead (`et(<store>,wv)` with
|
|
891
|
+
`wv=(s)=>s.streamingToolUses`) — same store, same fields, a third
|
|
892
|
+
spelling, and a fourth break in this one question while the other
|
|
893
|
+
thirteen steps rode through both restructures untouched. A matcher
|
|
894
|
+
enumerating idioms is a whitelist against a fashion, each branch buying
|
|
895
|
+
exactly one build.
|
|
896
|
+
|
|
897
|
+
What survives every spelling is the **twin**. Live thinking is the
|
|
898
|
+
sibling of live tool-uses — same store, same snapshot, same renderer —
|
|
899
|
+
and the tool-use half is a feature upstream ships working, so every build
|
|
900
|
+
must carry, in the render's own reach, a read of `streamingToolUses` off
|
|
901
|
+
the store. That read is the *production*, found as whichever of the field
|
|
902
|
+
name's two grammar positions this build spells — a snapshot pattern's own
|
|
903
|
+
key (the hook call its sole argument, since a second would make the
|
|
904
|
+
pattern something other than the snapshot), or a selector beside the
|
|
905
|
+
store (a function answering the field off its own parameter, inline or
|
|
906
|
+
hoisted behind a module-local name) — one answer or none per scope
|
|
907
|
+
(`js.only`), behind whatever `??` fallback the read wears (`js.values`;
|
|
908
|
+
2.1.247 arrived wearing one). The production is then *reused verbatim* —
|
|
909
|
+
the reuse-their-expression rule `org-label` and the extras memo already
|
|
910
|
+
follow — so the state is read the way this build reads its twin, with one
|
|
911
|
+
field name swapped, and upstream can only break the claim by breaking
|
|
912
|
+
their own feature. That is the anchor-selection rule this step's whole
|
|
913
|
+
history teaches: prefer claims upstream cannot drop without paying for
|
|
914
|
+
them — the dispatch strings, the store's field names, the twin's dataflow
|
|
915
|
+
— over claims only this patch needs; a neighbour prop, a handing site and
|
|
916
|
+
a storage idiom were all the second kind, and every break above was one
|
|
917
|
+
of them.
|
|
918
|
+
|
|
919
|
+
- **On a store build the rewrite is a subscription, not a value.**
|
|
920
|
+
Threading the state's value was sound while the resolved render was
|
|
921
|
+
live-computed, and 2.1.247 retired that render too: the surviving
|
|
922
|
+
conversation render is react-compiler cached behind a fixed slot-test
|
|
923
|
+
chain (`if(Yr[15]!==ph||…)Du=r(Ih,{…})`), and a prop the compiler never
|
|
924
|
+
saw is a prop no slot tests — the element is reused, the child bails on
|
|
925
|
+
identity, and a perfectly threaded value renders exactly once.
|
|
926
|
+
Parse-green and frozen, a failure class `doctor` cannot see. So the
|
|
927
|
+
render is rerouted through a module-scope wrapper component
|
|
928
|
+
(`__cc_LiveConversation`) spliced beside it: the bag gains
|
|
929
|
+
`__cc_stream:<store>,` (the production's own store argument, whose
|
|
930
|
+
identity is as fresh as the conversation the cache already tests), the
|
|
931
|
+
render's component argument becomes the wrapper, and the wrapper
|
|
932
|
+
subscribes itself through the production's own hook with a selector of
|
|
933
|
+
ours in upstream's per-field shape, handing the component
|
|
934
|
+
`streamingThinking:` beside the spread props. The hook's optional
|
|
935
|
+
selector parameter is upstream's since the store era began — 2.1.236's
|
|
936
|
+
hook and 2.1.247's are the same two-parameter function, measured — and
|
|
937
|
+
where the destructure spelling leaves it undemonstrated, an in-module
|
|
938
|
+
hook is held to it (resolved, and required to declare a second
|
|
939
|
+
parameter), because a hook that ignored the argument would thread the
|
|
940
|
+
whole snapshot with every count green; an *imported* hook is accepted on
|
|
941
|
+
the measurement, a residue accepted by name rather than a cross-module
|
|
942
|
+
resolver built for a build that has never shipped. The transcript
|
|
943
|
+
renderer's own memo comparator compares unknown props by
|
|
944
|
+
identity (since 2.1.210), so a fresh value re-renders it and a quiet one
|
|
945
|
+
does not, cached parent or not: a component re-rendering from its own
|
|
946
|
+
subscription is the one contract in this chain no minifier or compiler
|
|
947
|
+
rewrites. Every name the wrapper re-spells — the JSX callee, the
|
|
948
|
+
component, the hook — is read off the site and owed module-scope meaning
|
|
949
|
+
(`_unshadowed`): any binding of the spelling on the way — a parameter, a
|
|
950
|
+
declarator, a function's or class's declared name, a catch clause's
|
|
951
|
+
parameter — would make the wrapper name something else that parses,
|
|
952
|
+
verifies, and throws at first render, so it refuses the wrap loudly
|
|
953
|
+
instead, and the store expression's own spellings are walked the same
|
|
954
|
+
way, bounded at the scope upstream wrote them in. The witness is
|
|
955
|
+
unchanged, paid the way `thinking-summaries` pays
|
|
956
|
+
it: the field the wrapper selects must still be named by the bundle's own
|
|
957
|
+
objects (the store's snapshot initialiser, its publish call —
|
|
958
|
+
`streamingThinking:null`, upstream's own since 2.1.236), so a store that
|
|
959
|
+
renames the field reads as the step reporting the store rather than
|
|
960
|
+
subscribing to `undefined`. Two things still come free with the store:
|
|
961
|
+
its setter takes functional updaters — React's own contract, which the
|
|
962
|
+
reducer splices already speak — and it hides a finished block itself
|
|
963
|
+
after 30 s, upstream's own linger.
|
|
902
964
|
|
|
903
965
|
A render was once selected by *position* — the observation that the real sites
|
|
904
966
|
fall after the state's `useState` declaration. That is worth recording as a
|
|
@@ -21,8 +21,8 @@ matches means the file changed under you, not that a number moved.
|
|
|
21
21
|
|
|
22
22
|
## On disk now
|
|
23
23
|
|
|
24
|
-
The whole published span `2.1.210` → `2.1.
|
|
25
|
-
published — one pristine binary per version,
|
|
24
|
+
The whole published span `2.1.210` → `2.1.247` — 2.1.230 and 2.1.244 were never
|
|
25
|
+
published — one pristine binary per version, 36 in all. It straddles the
|
|
26
26
|
**2.1.242 code split** (INTERNALS.md): `2.1.242`/`2.1.243` are the first
|
|
27
27
|
many-module builds, and they jump ~35 MB over `2.1.241` for it. Several pairs
|
|
28
28
|
share a byte count
|
|
@@ -67,10 +67,12 @@ the whole file, `sha256sum <version>.orig`) and never the size:
|
|
|
67
67
|
| `2.1.243` | 378 MB | `4b0dafeedd0b469c41988e200036fd773e7553ba960349c9f02a82c6d1f2ba27` |
|
|
68
68
|
| `2.1.245` | 392 MB | `16ad2b94deaf7b29abed966d981c9991a47af0420f5be8ed4a3f83bea9f678bc` |
|
|
69
69
|
| `2.1.246` | 248 MB | `1a0a662dc1bb938eaec38545abce9a4a69113d7d7f7c5e1a553ea276617b906a` |
|
|
70
|
+
| `2.1.247` | 239 MB | `5fb321bf417ffc5cd4e3f36e7c9c7e029bf47aaa36d5621db979fcc5e6eabe15` |
|
|
70
71
|
|
|
71
72
|
This set covers the span the playbook's tree-move measurements were taken over
|
|
72
|
-
(`2.1.210` → `2.1.233`), the 2.1.242 split,
|
|
73
|
-
migration,
|
|
73
|
+
(`2.1.210` → `2.1.233`), the 2.1.242 split, the 2.1.246 stream-store
|
|
74
|
+
migration, and the 2.1.247 selector reads it was read back through, so each is
|
|
75
|
+
re-checkable here rather than historical.
|
|
74
76
|
|
|
75
77
|
## Rebuild or extend it
|
|
76
78
|
|
|
@@ -151,9 +151,14 @@ _TOOL_USES = "streamingToolUses"
|
|
|
151
151
|
#: build in the corpus: the pair already names the same one renderer.
|
|
152
152
|
_TRANSCRIPT_SIGNATURE = ("messages", _TOOL_USES)
|
|
153
153
|
#: Our local for the threaded state, wherever one has to be minted: the
|
|
154
|
-
#: renderer signatures `transcript-signature` extends, and the
|
|
155
|
-
#:
|
|
154
|
+
#: renderer signatures `transcript-signature` extends, and the live wrapper's
|
|
155
|
+
#: own subscription on builds that keep the state in a store.
|
|
156
156
|
_INJECTED = "__cc_streamingThinking"
|
|
157
|
+
#: The store handle the live wrapper reads, one prop of ours beside upstream's.
|
|
158
|
+
_STORE_PROP = "__cc_stream"
|
|
159
|
+
#: The component a store-era render is rerouted through: the subscription the
|
|
160
|
+
#: build makes for tool uses, made once more for thinking.
|
|
161
|
+
_WRAPPER = "__cc_LiveConversation"
|
|
157
162
|
|
|
158
163
|
|
|
159
164
|
def _outermost(scope: js.Node) -> bool:
|
|
@@ -239,55 +244,242 @@ def _bound_in_scope(site: js.Node, prop: str) -> str | None:
|
|
|
239
244
|
return None
|
|
240
245
|
|
|
241
246
|
|
|
242
|
-
def
|
|
243
|
-
"""
|
|
244
|
-
snapshot pattern to extend first when the state has to be read out of a
|
|
245
|
-
store rather than a binding upstream already made.
|
|
247
|
+
def _bound_names(pattern: js.Node | None) -> set[str]:
|
|
248
|
+
"""Every name a binding pattern binds -- and none it merely references.
|
|
246
249
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
+
A pattern's *keys* rename, its *defaults* are read, and only what lands on
|
|
251
|
+
the binding side is a name the scope owns; walking every identifier in the
|
|
252
|
+
subtree would claim both halves and refuse wraps over names nobody binds.
|
|
253
|
+
"""
|
|
254
|
+
if pattern is None:
|
|
255
|
+
return set()
|
|
256
|
+
if pattern.type in ("identifier", "shorthand_property_identifier_pattern"):
|
|
257
|
+
return {js.text(pattern)}
|
|
258
|
+
if pattern.type == "pair_pattern":
|
|
259
|
+
return _bound_names(pattern.child_by_field_name("value"))
|
|
260
|
+
if pattern.type in ("assignment_pattern", "object_assignment_pattern"):
|
|
261
|
+
return _bound_names(pattern.child_by_field_name("left"))
|
|
262
|
+
if pattern.type in (
|
|
263
|
+
"object_pattern",
|
|
264
|
+
"array_pattern",
|
|
265
|
+
"formal_parameters",
|
|
266
|
+
"rest_pattern",
|
|
267
|
+
):
|
|
268
|
+
found: set[str] = set()
|
|
269
|
+
for child in js.children(pattern):
|
|
270
|
+
found |= _bound_names(child)
|
|
271
|
+
return found
|
|
272
|
+
return set()
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _binds(scope: js.Node, name: str) -> bool:
|
|
276
|
+
"""Does this scope bind that spelling, by any binding form it has?
|
|
277
|
+
|
|
278
|
+
One home for the question two callers ask -- :func:`_unshadowed` to refuse
|
|
279
|
+
a wrap, :func:`_module_function` to stop a resolve -- because a binding
|
|
280
|
+
form the list misses is a hole in both at once. The forms are the
|
|
281
|
+
grammar's own: the scope's parameters, its declarators, and the
|
|
282
|
+
declarations that bind by existing -- a function's name, a class's, a
|
|
283
|
+
catch clause's parameter. The first version listed parameters, declarators
|
|
284
|
+
and function declarations, and independent review demonstrated the gap the
|
|
285
|
+
same day: a catch parameter and a class name are bindings too, and a
|
|
286
|
+
wrapper re-spelling either would parse, verify, and throw at first render.
|
|
287
|
+
"""
|
|
288
|
+
for holder in (
|
|
289
|
+
scope.child_by_field_name("parameters"),
|
|
290
|
+
scope.child_by_field_name("parameter"),
|
|
291
|
+
):
|
|
292
|
+
if name in _bound_names(holder):
|
|
293
|
+
return True
|
|
294
|
+
for declarator in js.every(scope, js.of_type("variable_declarator"), scoped=True):
|
|
295
|
+
if name in _bound_names(declarator.child_by_field_name("name")):
|
|
296
|
+
return True
|
|
297
|
+
for declaration in js.every(
|
|
298
|
+
scope,
|
|
299
|
+
js.of_type(
|
|
300
|
+
"function_declaration",
|
|
301
|
+
"generator_function_declaration",
|
|
302
|
+
"class_declaration",
|
|
303
|
+
),
|
|
304
|
+
scoped=True,
|
|
305
|
+
):
|
|
306
|
+
bound = declaration.child_by_field_name("name")
|
|
307
|
+
if bound is not None and js.text(bound) == name:
|
|
308
|
+
return True
|
|
309
|
+
for clause in js.every(scope, js.of_type("catch_clause"), scoped=True):
|
|
310
|
+
if name in _bound_names(clause.child_by_field_name("parameter")):
|
|
311
|
+
return True
|
|
312
|
+
return False
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def _module_function(source: Source, local: js.Node) -> js.Node | None:
|
|
316
|
+
"""The function this spelling means *at this use*, resolved lexically.
|
|
317
|
+
|
|
318
|
+
A selector is usually hoisted (``function wv(sL){return
|
|
319
|
+
sL.streamingToolUses}``) and its call site carries only the minified name,
|
|
320
|
+
which is a spelling until its scope is said: in the monolith the same two
|
|
321
|
+
letters bind functions in a hundred unrelated scopes. So the walk out from
|
|
322
|
+
the use asks each scope in turn, and the innermost binding decides: a
|
|
323
|
+
function bound there is the answer, and any *other* binding of the name
|
|
324
|
+
blocks the resolve -- the engine would resolve the spelling to that
|
|
325
|
+
binding, not to a function further out, and the first version skipped
|
|
326
|
+
non-function bindings while claiming the engine's answer. Only when no
|
|
327
|
+
enclosing scope binds the name at all does the single module-scope
|
|
328
|
+
function answer from afar; anything still ambiguous answers ``None``.
|
|
329
|
+
This is a predicate's resolver, not a locator: an unresolvable name here
|
|
330
|
+
just means "not a provable selector", and the loudness belongs to the
|
|
331
|
+
step whose production then fails to resolve.
|
|
332
|
+
"""
|
|
333
|
+
name = js.text(local)
|
|
334
|
+
owned: dict[int, js.Node] = {}
|
|
335
|
+
module_scope: dict[int, js.Node] = {}
|
|
336
|
+
for node in source.find_local(local, name):
|
|
337
|
+
parent = node.parent
|
|
338
|
+
if parent is None:
|
|
339
|
+
continue
|
|
340
|
+
fn = None
|
|
341
|
+
if (
|
|
342
|
+
parent.type == "function_declaration"
|
|
343
|
+
and parent.child_by_field_name("name") == node
|
|
344
|
+
):
|
|
345
|
+
fn = parent
|
|
346
|
+
elif (
|
|
347
|
+
parent.type == "variable_declarator"
|
|
348
|
+
and parent.child_by_field_name("name") == node
|
|
349
|
+
and (value := parent.child_by_field_name("value")) is not None
|
|
350
|
+
and value.type in js.FUNCTIONS
|
|
351
|
+
):
|
|
352
|
+
fn = value
|
|
353
|
+
if fn is None:
|
|
354
|
+
continue
|
|
355
|
+
owner = js.climb(fn.parent, lambda n: n.type in js.FUNCTIONS)
|
|
356
|
+
if owner is None or _outermost(owner):
|
|
357
|
+
module_scope[fn.id] = fn
|
|
358
|
+
else:
|
|
359
|
+
owned[owner.id] = fn
|
|
360
|
+
scope = js.climb(local, lambda n: n.type in js.FUNCTIONS)
|
|
361
|
+
while scope is not None and not _outermost(scope):
|
|
362
|
+
if scope.id in owned:
|
|
363
|
+
return owned[scope.id]
|
|
364
|
+
if _binds(scope, name):
|
|
365
|
+
return None
|
|
366
|
+
scope = js.climb(scope.parent, lambda n: n.type in js.FUNCTIONS)
|
|
367
|
+
found = list(module_scope.values())
|
|
368
|
+
return found[0] if len(found) == 1 else None
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def _selects(source: Source, argument: js.Node, field: str) -> bool:
|
|
372
|
+
"""Is this argument a *selector* for that snapshot field?
|
|
373
|
+
|
|
374
|
+
A selector is a function answering the field read off its own parameter --
|
|
375
|
+
2.1.247's ``function wv(sL){return sL.streamingToolUses}``, handed to the
|
|
376
|
+
store hook beside the store. The field name is the identity, in the one
|
|
377
|
+
grammar position a per-field read gives it; whether the function is inline
|
|
378
|
+
or hoisted behind a name is spelling (:func:`_module_function`), and the
|
|
379
|
+
answer routes through :func:`js.values` like every other answer here.
|
|
380
|
+
"""
|
|
381
|
+
fn = argument
|
|
382
|
+
if argument.type == "identifier":
|
|
383
|
+
resolved = _module_function(source, argument)
|
|
384
|
+
if resolved is None:
|
|
385
|
+
return False
|
|
386
|
+
fn = resolved
|
|
387
|
+
if fn.type not in js.FUNCTIONS:
|
|
388
|
+
return False
|
|
389
|
+
taken = [js.binding(parameter) for parameter in js.positional(fn)]
|
|
390
|
+
if len(taken) != 1:
|
|
391
|
+
return False
|
|
392
|
+
param = js.text(taken[0])
|
|
393
|
+
block = js.body(fn)
|
|
394
|
+
if block is None:
|
|
395
|
+
return False
|
|
396
|
+
answers = (
|
|
397
|
+
[block]
|
|
398
|
+
if block.type != "statement_block"
|
|
399
|
+
else [
|
|
400
|
+
expression
|
|
401
|
+
for statement in js.every(fn, js.of_type("return_statement"), scoped=True)
|
|
402
|
+
for expression in js.children(statement)
|
|
403
|
+
]
|
|
404
|
+
)
|
|
405
|
+
return any(
|
|
406
|
+
js.reads(value, field) and js.text(js.receiver(value)) == param
|
|
407
|
+
for answer in answers
|
|
408
|
+
for value in js.values(answer)
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
@dataclass(slots=True)
|
|
413
|
+
class _PairState:
|
|
414
|
+
"""The component-owned state (through 2.1.235): thread its value."""
|
|
415
|
+
|
|
416
|
+
state: str
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
@dataclass(slots=True)
|
|
420
|
+
class _StoreRead:
|
|
421
|
+
"""A store-era production: wrap the render with a subscription through it.
|
|
422
|
+
|
|
423
|
+
``selected`` records whether the spelling itself demonstrated the hook
|
|
424
|
+
taking a selector -- what licenses :func:`_wrap` to skip its arity check.
|
|
425
|
+
"""
|
|
426
|
+
|
|
427
|
+
production: js.Node
|
|
428
|
+
store: js.Node
|
|
429
|
+
selected: bool
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def _state_in_scope(source: Source, site: js.Node) -> _PairState | _StoreRead | None:
|
|
433
|
+
"""The live-thinking state at this render: how this build's own scope
|
|
434
|
+
reaches the live stream data, resolved from the site outwards.
|
|
435
|
+
|
|
436
|
+
Outwards, so what is spliced is valid where it is spliced: on 2.1.232 the
|
|
250
437
|
two conversation renders sit in *different* top-level components, only one
|
|
251
|
-
of which declares the state, and
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
- **
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
(``{streamingToolUses:…}=useX(<store>)
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
438
|
+
of which declares the state, and a single bundle-wide answer put an
|
|
439
|
+
out-of-scope identifier into a shipped binary -- it parses, so no gate
|
|
440
|
+
could see it, and it throws when that component renders. A binding used
|
|
441
|
+
here has to be one this render can *see* (:func:`js.visible`).
|
|
442
|
+
|
|
443
|
+
The state has two semantic homes, and each is identified by the only name
|
|
444
|
+
it has:
|
|
445
|
+
|
|
446
|
+
- **The component's own pair** -- every build through 2.1.235. Nothing
|
|
447
|
+
names the pair itself (``useState(null)`` and ``useState(void 0)`` are
|
|
448
|
+
the same state, so the initialiser is never asked); what names it is its
|
|
449
|
+
*setter*, handed to the reducer as ``onStreamingThinking:`` in this very
|
|
450
|
+
scope. The state is the array pattern whose second binding is a handed
|
|
451
|
+
setter, and the handing is asked of the whole subtree on purpose -- a
|
|
452
|
+
scope may hand it from inside a callback, and that says nothing about
|
|
453
|
+
where the state lives. Answered as a :class:`_PairState`: the state is
|
|
454
|
+
a binding of this scope, and threading its value is the whole rewrite.
|
|
455
|
+
- **The stream store** -- 2.1.236 on. Here the question this function used
|
|
456
|
+
to ask -- *which idiom holds the state* -- is retired, because it broke
|
|
457
|
+
on every answer it ever gave: the whole-snapshot destructure it knew
|
|
458
|
+
(``{streamingToolUses:…}=useX(<store>)``, 2.1.236-2.1.246) was retired
|
|
459
|
+
by 2.1.247's per-field selector reads (``et(<store>,wv)``), the handing
|
|
460
|
+
it once rested on moved into the engine on 2.1.246, and each repair
|
|
461
|
+
bought exactly one build. Those are spellings of React's data-access
|
|
462
|
+
fashion, the busiest surface upstream owns, and a matcher enumerating
|
|
463
|
+
them is a whitelist against a fashion. What survives every spelling is
|
|
464
|
+
the **twin**: live tool-uses ride the same store, and that feature ships
|
|
465
|
+
working, so somewhere in this scope upstream reads ``streamingToolUses``
|
|
466
|
+
off the store -- as a snapshot pattern's own key, or through a selector
|
|
467
|
+
handed to the hook beside the store. That read is the **production**,
|
|
468
|
+
answered as a :class:`_StoreRead`: the hook and the store, upstream's
|
|
469
|
+
own, read out of whatever spelling this build uses and reused verbatim
|
|
470
|
+
by :func:`_wrap` -- the same
|
|
471
|
+
reuse-their-expression rule `org-label` and the extras memo follow,
|
|
472
|
+
because what is copied cannot drift from what it was copied from.
|
|
473
|
+
|
|
474
|
+
A production is a declarator this site can see whose value is one call
|
|
475
|
+
among its possible values (:func:`js.values` -- 2.1.247 arrived with a
|
|
476
|
+
``??`` fallback on the read, and the exact-node question would have read
|
|
477
|
+
it as no call at all), taking the store alone (the destructure spelling:
|
|
478
|
+
the pattern names the field as its own key, sole argument since a second
|
|
479
|
+
would make the pattern something other than the snapshot) or the store
|
|
480
|
+
beside exactly one selector for the field (the selector spelling). One
|
|
481
|
+
answer or none per scope (:func:`js.only`): two reads of the store is a
|
|
482
|
+
cardinality change to be told about, not a first to win.
|
|
291
483
|
"""
|
|
292
484
|
scope = js.climb(site, lambda n: n.type in js.FUNCTIONS)
|
|
293
485
|
while scope is not None and not _outermost(scope):
|
|
@@ -300,7 +492,7 @@ def _state_in_scope(site: js.Node) -> tuple[str, js.Node | None] | None:
|
|
|
300
492
|
if (pair := js.named(node)) is not None
|
|
301
493
|
and (value := pair.child_by_field_name("value")) is not None
|
|
302
494
|
}
|
|
303
|
-
|
|
495
|
+
productions: dict[int, _StoreRead] = {}
|
|
304
496
|
for declarator in js.every(scope, js.of_type("variable_declarator")):
|
|
305
497
|
name = declarator.child_by_field_name("name")
|
|
306
498
|
if name is None or not js.visible(declarator, site):
|
|
@@ -308,49 +500,209 @@ def _state_in_scope(site: js.Node) -> tuple[str, js.Node | None] | None:
|
|
|
308
500
|
if name.type == "array_pattern":
|
|
309
501
|
bound = [js.text(child) for child in name.named_children]
|
|
310
502
|
if len(bound) == 2 and bound[1] in setters:
|
|
311
|
-
return bound[0]
|
|
312
|
-
continue
|
|
313
|
-
value = declarator.child_by_field_name("value")
|
|
314
|
-
if name.type != "object_pattern" or not js.children(name):
|
|
315
|
-
continue
|
|
316
|
-
if value is None or value.type != "call_expression":
|
|
503
|
+
return _PairState(bound[0])
|
|
317
504
|
continue
|
|
318
|
-
|
|
505
|
+
calls = [
|
|
506
|
+
v
|
|
507
|
+
for v in js.values(declarator.child_by_field_name("value"))
|
|
508
|
+
if v.type == "call_expression"
|
|
509
|
+
]
|
|
510
|
+
if len(calls) != 1:
|
|
319
511
|
continue
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
512
|
+
production = calls[0]
|
|
513
|
+
args = js.arguments(production)
|
|
514
|
+
if name.type == "object_pattern" and js.children(name):
|
|
515
|
+
if _TOOL_USES not in js.props(name) or len(args) != 1:
|
|
516
|
+
continue
|
|
517
|
+
productions[declarator.id] = _StoreRead(production, args[0], False)
|
|
518
|
+
elif name.type == "identifier" and len(args) == 2:
|
|
519
|
+
chosen = [a for a in args if _selects(source, a, _TOOL_USES)]
|
|
520
|
+
if len(chosen) == 1:
|
|
521
|
+
store = args[0] if chosen[0] == args[1] else args[1]
|
|
522
|
+
productions[declarator.id] = _StoreRead(production, store, True)
|
|
523
|
+
found = js.only(list(productions.values()), "reads of the live stream store")
|
|
524
|
+
if found is not None:
|
|
525
|
+
return found
|
|
331
526
|
scope = js.climb(scope.parent, lambda n: n.type in js.FUNCTIONS)
|
|
332
527
|
return None
|
|
333
528
|
|
|
334
529
|
|
|
530
|
+
def _module_statement(node: js.Node) -> js.Node:
|
|
531
|
+
"""The module-scope statement this node is part of -- where a declaration
|
|
532
|
+
meant for the whole module goes.
|
|
533
|
+
|
|
534
|
+
Module scope is a fact about the build, not a fixed depth: a split
|
|
535
|
+
module's ``program`` is the scope, and the monolith's is the wrapper
|
|
536
|
+
IIFE's own body (:func:`_outermost` -- inserting before the IIFE itself
|
|
537
|
+
would put the declaration outside every binding it needs).
|
|
538
|
+
"""
|
|
539
|
+
statement = node
|
|
540
|
+
while statement.parent is not None:
|
|
541
|
+
parent = statement.parent
|
|
542
|
+
if parent.type == "program":
|
|
543
|
+
return statement
|
|
544
|
+
if parent.type == "statement_block":
|
|
545
|
+
owner = js.climb(parent, lambda n: n.type in js.FUNCTIONS)
|
|
546
|
+
if owner is not None and _outermost(owner):
|
|
547
|
+
return statement
|
|
548
|
+
statement = parent
|
|
549
|
+
return statement
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
def _root(expression: js.Node) -> js.Node | None:
|
|
553
|
+
"""The identifier an expression resolves through -- ``X`` in
|
|
554
|
+
``X.createElement`` -- or nothing when there is no lone name to check."""
|
|
555
|
+
node: js.Node | None = expression
|
|
556
|
+
while node is not None and node.type in (
|
|
557
|
+
"member_expression",
|
|
558
|
+
"subscript_expression",
|
|
559
|
+
"parenthesized_expression",
|
|
560
|
+
):
|
|
561
|
+
node = node.child_by_field_name("object") or next(iter(js.children(node)), None)
|
|
562
|
+
return node if node is not None and node.type == "identifier" else None
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
def _unshadowed(name: str, site: js.Node, *, above: js.Node | None = None) -> bool:
|
|
566
|
+
"""Does this spelling keep its meaning from the site up to ``above``?
|
|
567
|
+
|
|
568
|
+
The live wrapper is a module-scope function that re-spells names it read
|
|
569
|
+
off the render -- the JSX callee, the component, the hook -- and a
|
|
570
|
+
component-scope binding of the same spelling between the two would make
|
|
571
|
+
the wrapper name something else entirely: it parses, verifies, and throws
|
|
572
|
+
when the wrapper first renders, the 2.1.232 class of damage. So the name
|
|
573
|
+
is walked from the site outwards, and any scope that binds it
|
|
574
|
+
(:func:`_binds`, every binding form at once) refuses the wrap loudly
|
|
575
|
+
rather than shipping it. With no ``above`` the walk runs to module scope,
|
|
576
|
+
for a name the wrapper re-spells there; the store expression's spellings
|
|
577
|
+
name ``above`` instead -- the scope upstream wrote them in -- because a
|
|
578
|
+
spelling copied *down* into the bag only has to mean at the bag what it
|
|
579
|
+
meant where it was copied from.
|
|
580
|
+
"""
|
|
581
|
+
scope = js.climb(site, lambda n: n.type in js.FUNCTIONS)
|
|
582
|
+
while scope is not None and not _outermost(scope):
|
|
583
|
+
if above is not None and scope.id == above.id:
|
|
584
|
+
return True
|
|
585
|
+
if _binds(scope, name):
|
|
586
|
+
return False
|
|
587
|
+
scope = js.climb(scope.parent, lambda n: n.type in js.FUNCTIONS)
|
|
588
|
+
return True
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
def _wrap(
|
|
592
|
+
source: Source, bag: js.Node, resolved: _StoreRead, serial: int
|
|
593
|
+
) -> list[Edit] | str:
|
|
594
|
+
"""Reroute this render through a component of ours that subscribes itself.
|
|
595
|
+
|
|
596
|
+
Threading the state's *value* into the props bag was sound while the
|
|
597
|
+
resolved render was live-computed, and 2.1.247 retired that render: the
|
|
598
|
+
surviving one is react-compiler cached behind a fixed slot-test chain
|
|
599
|
+
(``if(Yr[15]!==ph||…)Du=r(Ih,{…})``), and a prop the compiler never saw is
|
|
600
|
+
a prop no slot tests -- the element is reused, the child bails out on
|
|
601
|
+
identity, and a perfectly threaded value renders exactly once. The
|
|
602
|
+
compiler's cache is a representation this patch refuses to model; what
|
|
603
|
+
needs no modelling is React's own contract that a component re-renders
|
|
604
|
+
from its own subscription. So the render is rerouted through a wrapper
|
|
605
|
+
that makes the subscription this scope stopped making: it reads the store
|
|
606
|
+
through the *production's own hook* -- whose optional selector parameter
|
|
607
|
+
is upstream's since the store era began (2.1.236's hook and 2.1.247's are
|
|
608
|
+
the same two-parameter function, measured) -- with a selector of ours in
|
|
609
|
+
upstream's own per-field shape, and hands the component the state under
|
|
610
|
+
the prop every other step already speaks. The transcript renderer's own
|
|
611
|
+
memo comparator compares unknown props by identity (and has since
|
|
612
|
+
2.1.210), so a fresh value re-renders it and a quiet one does not, cached
|
|
613
|
+
parent or not.
|
|
614
|
+
|
|
615
|
+
Everything the wrapper spells is read off the site -- the JSX callee, the
|
|
616
|
+
component, the hook -- so the one thing owed is that those names mean at
|
|
617
|
+
module scope what they mean at the render (:func:`_unshadowed`); a
|
|
618
|
+
shadowed name refuses the wrap loudly, and the answer is the *reason*, so
|
|
619
|
+
the step's note names what refused rather than one sentence covering
|
|
620
|
+
three facts. The store expression is not module-scope and never has to
|
|
621
|
+
be: it rides into the props bag (``__cc_stream:``), evaluated exactly
|
|
622
|
+
where the old threading evaluated its state, its spellings walked the
|
|
623
|
+
same way but bounded at the scope upstream wrote them in, and the bag
|
|
624
|
+
being cached is harmless for it -- the store's identity is as fresh as
|
|
625
|
+
the conversation the cache already tests. A bag that is not a JSX props
|
|
626
|
+
argument (the render call's second argument) is refused the same way:
|
|
627
|
+
rerouting a call this step misread would be rubble, not a wrap.
|
|
628
|
+
|
|
629
|
+
Where the destructure spelling leaves the selector undemonstrated, an
|
|
630
|
+
in-module hook is held to it -- resolved and required to declare a second
|
|
631
|
+
parameter -- because a hook that ignores the argument would thread the
|
|
632
|
+
whole snapshot as the state with every count green, the one silent
|
|
633
|
+
failure this rewrite could otherwise add. A hook defined elsewhere is
|
|
634
|
+
accepted on the measurement above: following the import hop would mean
|
|
635
|
+
cross-module function resolution built for a build that has never
|
|
636
|
+
shipped, and that residue -- a future destructure build importing a
|
|
637
|
+
selectorless hook -- is accepted here by name rather than guessed at.
|
|
638
|
+
"""
|
|
639
|
+
handed = bag.parent
|
|
640
|
+
render = handed.parent if handed is not None else None
|
|
641
|
+
if render is None:
|
|
642
|
+
return "the conversation bag is not a component call's props"
|
|
643
|
+
args = js.arguments(render)
|
|
644
|
+
if len(args) < 2 or args[1] != bag:
|
|
645
|
+
return "the conversation bag is not a component call's props"
|
|
646
|
+
component = args[0]
|
|
647
|
+
jsx = render.child_by_field_name("function")
|
|
648
|
+
hook = resolved.production.child_by_field_name("function")
|
|
649
|
+
if jsx is None or hook is None:
|
|
650
|
+
return "the conversation bag is not a component call's props"
|
|
651
|
+
for expression in (jsx, component, hook):
|
|
652
|
+
root = _root(expression)
|
|
653
|
+
if root is None or not _unshadowed(js.text(root), bag):
|
|
654
|
+
return "a name the live wrapper needs is not the render's to use"
|
|
655
|
+
origin = js.climb(resolved.production, lambda n: n.type in js.FUNCTIONS)
|
|
656
|
+
references = {
|
|
657
|
+
js.text(node) for node in js.every(resolved.store, js.of_type("identifier"))
|
|
658
|
+
}
|
|
659
|
+
if resolved.store.type == "identifier":
|
|
660
|
+
references.add(js.text(resolved.store))
|
|
661
|
+
if origin is not None and any(
|
|
662
|
+
not _unshadowed(reference, bag, above=origin) for reference in references
|
|
663
|
+
):
|
|
664
|
+
return "the store's spelling does not reach the render"
|
|
665
|
+
if not resolved.selected and hook.type == "identifier":
|
|
666
|
+
defined = _module_function(source, hook)
|
|
667
|
+
if defined is not None and len(js.positional(defined)) < 2:
|
|
668
|
+
return "the store hook takes no selector on this build"
|
|
669
|
+
name = _WRAPPER if serial == 0 else f"{_WRAPPER}{serial + 1}"
|
|
670
|
+
declaration = (
|
|
671
|
+
f"function {name}(__cc_props){{"
|
|
672
|
+
f"let {_INJECTED}={js.text(hook)}(__cc_props.{_STORE_PROP},"
|
|
673
|
+
f"(__cc_snapshot)=>__cc_snapshot.{_STREAMING});"
|
|
674
|
+
f"return {js.text(jsx)}({js.text(component)},"
|
|
675
|
+
f"{{...__cc_props,{_STREAMING}:{_INJECTED}}})}}"
|
|
676
|
+
)
|
|
677
|
+
return [
|
|
678
|
+
Edit.before(_module_statement(render), declaration),
|
|
679
|
+
Edit.replace(component, name),
|
|
680
|
+
Edit.before(
|
|
681
|
+
js.entry(js.props(bag)[_CONVERSATION[0]]),
|
|
682
|
+
f"{_STORE_PROP}:{js.text(resolved.store)},",
|
|
683
|
+
),
|
|
684
|
+
]
|
|
685
|
+
|
|
686
|
+
|
|
335
687
|
def _step_prop_threading(source: Source, outcome: Outcome) -> Source:
|
|
336
688
|
"""Pass the live-thinking state into the renderers that need it."""
|
|
337
689
|
step = outcome.step("prop-threading")
|
|
338
|
-
edits = []
|
|
690
|
+
edits: list[Edit] = []
|
|
339
691
|
renders = _conversation_renders(source)
|
|
340
|
-
# The durable witness behind a
|
|
341
|
-
# one the bundle's own objects still name (the store's snapshot
|
|
342
|
-
# and its publish call
|
|
343
|
-
#
|
|
344
|
-
#
|
|
692
|
+
# The durable witness behind a store read: the field the wrapper selects
|
|
693
|
+
# is one the bundle's own objects still name (the store's snapshot
|
|
694
|
+
# initialiser and its publish call -- `streamingThinking:null`, upstream's
|
|
695
|
+
# own since 2.1.236). A store that renames the field would leave the
|
|
696
|
+
# wrapper subscribing to `undefined` with every count green -- the same
|
|
345
697
|
# hole `thinking-summaries` pays a header-name count for.
|
|
346
698
|
field_named = any(
|
|
347
699
|
(pair := js.named(node)) is not None and pair.type == "pair"
|
|
348
700
|
for node in source.find(_STREAMING)
|
|
349
701
|
)
|
|
350
|
-
|
|
702
|
+
wrapped = 0
|
|
351
703
|
unreached = 0
|
|
352
704
|
for bag in renders:
|
|
353
|
-
resolved = _state_in_scope(bag)
|
|
705
|
+
resolved = _state_in_scope(source, bag)
|
|
354
706
|
if resolved is None:
|
|
355
707
|
# By design on every build we hold: the resume view, the transcript
|
|
356
708
|
# overlay and the message picker draw conversations too, in scopes
|
|
@@ -360,26 +712,33 @@ def _step_prop_threading(source: Source, outcome: Outcome) -> Source:
|
|
|
360
712
|
# 2.1.232 out-of-scope threading).
|
|
361
713
|
unreached += 1
|
|
362
714
|
continue
|
|
363
|
-
state, pattern = resolved
|
|
364
|
-
if pattern is not None and not field_named:
|
|
365
|
-
step.note(f"the stream store no longer names a {_STREAMING} field")
|
|
366
|
-
continue
|
|
367
|
-
step.candidates += 1
|
|
368
|
-
step.applied += 1
|
|
369
715
|
carried = js.props(bag)
|
|
370
716
|
if _STREAMING in carried:
|
|
371
717
|
# Upstream already threads it into this render; the goal achieved.
|
|
718
|
+
step.candidates += 1
|
|
719
|
+
step.applied += 1
|
|
372
720
|
continue
|
|
373
|
-
if
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
extended.add(pattern.id)
|
|
721
|
+
if isinstance(resolved, _PairState):
|
|
722
|
+
step.candidates += 1
|
|
723
|
+
step.applied += 1
|
|
377
724
|
edits.append(
|
|
378
|
-
Edit.before(
|
|
725
|
+
Edit.before(
|
|
726
|
+
js.entry(carried[_CONVERSATION[0]]),
|
|
727
|
+
f"{_STREAMING}:{resolved.state},",
|
|
728
|
+
)
|
|
379
729
|
)
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
730
|
+
continue
|
|
731
|
+
if not field_named:
|
|
732
|
+
step.note(f"the stream store no longer names a {_STREAMING} field")
|
|
733
|
+
continue
|
|
734
|
+
step.candidates += 1
|
|
735
|
+
wrap = _wrap(source, bag, resolved, wrapped)
|
|
736
|
+
if isinstance(wrap, str):
|
|
737
|
+
step.note(wrap)
|
|
738
|
+
continue
|
|
739
|
+
step.applied += 1
|
|
740
|
+
wrapped += 1
|
|
741
|
+
edits += wrap
|
|
383
742
|
# Printed every run, green ones included: an early warning held back until
|
|
384
743
|
# something breaks arrives too late to be one.
|
|
385
744
|
step.note(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|