Commit 142ce37
feat(graph): a fragment may declare a loop, not only a node (ADR 0027) (#186)
* docs(adr): the reusable unit is a loop, not a node
ADR 0027. A fragment may declare `nodes:` plus a required `exit:`, so a QA
loop or a review loop is citable with `use:` the way a single node is
today. ADR 0013's rule is generalized, not weakened: a fragment may never
name an id it does not itself declare, of which "a single-node fragment
may not declare depends_on/feedback" is the special case.
Measured 2026-08-16 across the 8 shipped graphs and the 18 operator
one-shot lanes: 12 of 18 lanes cite `use: pr-publish` and every one of
those 12 hand-writes the `review -> apply` pair immediately upstream of
the node it cited. Reuse is taken to exactly the depth the mechanism
allows and stops there.
Corrects the commissioning brief's first figure: `feedback:` is declared
by 2 of 8 shipped graphs (not 6) and 0 of 18 lanes. The repeating loop is
overwhelmingly a hand-unrolled one, not a declared arc, which is why the
unit spliced here is a subgraph rather than a loop primitive.
Records three findings the design brief did not carry:
- `graph.feedbackTokenPattern` must gain `/` alongside
`handoff.placeholderPattern`, or a spliced `{{ feedback.a/b }}` escapes
the load-time confinement check and is silently empty forever;
- `graph.nodeIDPattern` must admit the joined form, so the
cannot-collide property is restored by a loader refusal rather than
falling out of the charset;
- `internal/serve` and the dry-run seed re-spell the artifact path
without `sanitizeNodeID`, and would render 204 for a spliced node that
has a result.
Co-Authored-By: oh-my-graph <graphs@oh-my-graph.dev>
Signed-off-by: jitokim <pigberger70@gmail.com>
* docs(adr): a namespace that collides on disk is not a namespace
ADR 0027, revised under design review before any code exists. Two
critical corrections, both of which would have shipped a silently wrong
run, plus seven smaller ones.
`handoff.sanitizeNodeID` is NOT injective — it was merely the identity,
because no valid id could contain `/`. Minting `/` ids makes `a` + `b_c`
and `a_b` + `c` both persist to `a_b_c.out`, and a hand-written
`qa-a_impl` share a file with a spliced `qa-a/impl`. The ids differ, so
uniqueness validation passes; the FILE collides, the later node
overwrites, and `{{ artifacts.x | inline }}` inlines someone else's
output into a paid prompt. This is the exact class `feedback/` and
`failed/` exist to dodge, with the reason already in the code. The
separator's on-disk spelling therefore becomes `~` — outside the id
charset, injective over every id the loader admits, and the identity on
every file that exists today. Keeping `_` plus a duplicate-sanitized-id
load error was rejected: it makes a fragment's validity depend on what
else the host graph contains, which is verbatim why flat ids are
rejected.
`{{ artifacts.<using-id> }}` now rewrites to the exit, symmetrically with
`depends_on`. Without it the promised corpus conversion is impossible and
fails at spend rather than at load, since `run` never invokes the handoff
lint sweeps. The consequence is stated: a loop exposes exactly one value
outward.
Also settled:
- rewrite applies to fragment-body tokens BEFORE `with:` substitution;
a bound value is never rewritten, and one naming no declared id is a
load error (self-dev binds `{{ artifacts.e2e }}` today);
- the planner needs its own `*PlanError` refusal — `Plan` goes through
`graph.Parse`, not `LoadFile`, and `validatePlannedNodes` never checks
id shape, so the guarantee is three-way: loader, coordinator, Validate
as backstop;
- `rerun: qa-a/impl` is a load error (the non-goals blessed it while the
failure modes refused it), and `rerun: qa-a` is a load error too rather
than a silent rewrite to the exit — a request half-granted without a
word;
- `exit:` may not sit strictly inside one of the fragment's own feedback
bodies, so downstream wiring cannot manufacture a side exit the
fragment author cannot see; the remaining host-triggerable body checks
are enumerated;
- the conversion proof moves to `backlog-batch.yaml`. ADR 0013's frozen
equivalence gate keys its mask by node id and covers ids and edges, so
converting self-dev/dev-review-pr would retire that one-time evidence;
this ADR declines to. Those two cannot be converted anyway — `pr`
consumes two internal artifacts;
- an internal node may declare `type: gate`, with its three consequences
named;
- author-supplied ids (`prefix:` or an id map) are weighed for the first
time and rejected: cheaper by three refusals and a sanitizer change,
but it makes a loop's internals ordinary addressable ids, which is the
unit this ADR exists to create.
Co-Authored-By: oh-my-graph <graphs@oh-my-graph.dev>
Signed-off-by: jitokim <pigberger70@gmail.com>
* feat(graph): a namespaced id is spellable by the splicer alone
First slice of ADR 0027 (commit granularity: one commit per feature
slice — this is the charset move and the three compensations that must
land with it; the multi-node loader follows).
A multi-node fragment splices `<using-id>/<internal-id>` ids, so four
things move together, and one of them fails silently if forgotten:
- nodeIDPattern admits at most one '/', each side an otherwise-valid
segment. Validate accepts the joined form as the BACKSTOP it is: it
cannot tell a spliced graph from a hand-written one, and a resumed
leg re-parses a snapshot already full of joined ids.
- handoff.placeholderPattern gains '/' so a spliced
{{ artifacts.qa-a/impl }} resolves instead of shipping verbatim into
a paid prompt.
- graph.feedbackTokenPattern gains '/' in the SAME change, per the
invariant its own comment states. Alone, the previous change would
have let {{ feedback.qa-a/review }} interpolate at run time while
staying invisible to validateFeedbackPlaceholders — and an
unconfined feedback token does not fail, it is empty forever.
- the id refusal moves to the two places an id is WRITTEN rather than
read: the coordinator refuses a '/' in any id the planner produced
(the loader's half lands with the multi-node splice).
sanitizeNodeID becomes the exported, INJECTIVE SanitizeNodeID: '/' maps
to '~', not '_'. With '_', `a` + `b_c`, `a_b` + `c` and a hand-written
`a_b_c` all land on a_b_c.out — distinct ids, one file, whichever node
finishes last overwriting, and `| inline` reading another node's reply
into a paid prompt with nothing failing. '~' is outside the id charset,
so the map is injective over every id the loader admits, and it is still
the identity on every id that could exist before this change: no
artifact file moves and no resume is disturbed.
serve's /api/result and `run --dry-run`'s seeded paths stop re-spelling
`<node-id>.out` and route through the sanitizer, or serve would answer
204 "no result yet" for a node that has a result.
Co-Authored-By: oh-my-graph <graphs@oh-my-graph.dev>
Signed-off-by: jitokim <pigberger70@gmail.com>
* feat(graph): a fragment may declare a loop, not only a node
Second slice of ADR 0027: the loader learns the multi-node form. A
fragment file declares EITHER `node:` — unchanged in every respect, with
every one of its tests kept as the regression proof — or `nodes:` plus a
required `exit:`, and one sentence covers both: a fragment may never
name an id it does not itself declare. A single-node fragment declares
none, so depends_on/feedback stay load errors for it; a multi-node one
declares its own, so edges among THOSE are legal and nothing else is.
The old rule is now the special case, arithmetically.
Resolution, in the order it happens:
- a '/' in any id a FILE spells is refused BEFORE any splice, since
that is the only moment an authored id and a minted one are
distinguishable;
- each internal node is namespaced <using-id>/<internal-id> — its id,
its depends_on, its feedback.rerun, and every artifact/feedback
token in its text — and only THEN substituted, so a value bound at
the using site is never rewritten. Substitute-then-rewrite would
silently re-point a binding whenever the citing graph's id happened
to match one the fragment declares, which is a working reference
aimed at someone else's node;
- entry nodes (no internal parent) inherit the using node's
depends_on; cwd/worktree stay on the using node and propagate to
every spliced node, which is what backlog-batch writes by hand;
- from outside, the loop is one thing whose value is its exit's: both
`depends_on: [qa-a]` and `{{ artifacts.qa-a }}` resolve to
`qa-a/<exit>`. `rerun: qa-a` does NOT — rewritten to the exit, an
author asking to re-run a loop would silently get one node re-run.
`exit:` is required and never inferred from the unique sink: inference
is right only while there is exactly one, and when it is wrong it is
wrong silently. It may not lie strictly inside one of the fragment's own
feedback bodies, so no citing graph's downstream edge can manufacture a
side exit in a fragment whose author wrote nothing wrong.
A multi-node use declares wiring only — a behavior key on it is a load
error naming the key, because there is no coherent way to overlay one
node's success_check onto five.
Tests: the two-lane happy path (ids, inheritance, propagation, both
rewrites, no collision between two uses of one fragment, distinct
artifact files), the binding-namespace order trap, and 21 refusals —
every one asserted through both LoadFile and LintFile so the two views
cannot disagree about which problem comes first.
Co-Authored-By: oh-my-graph <graphs@oh-my-graph.dev>
Signed-off-by: jitokim <pigberger70@gmail.com>
* refactor(graphs): adr-driven-dev cites the round it wrote out twice
Third slice of ADR 0027: the corpus proof. `adr-driven-dev` unrolled
review → apply twice (round1 → apply1 → round2 → apply2); those four
nodes are now two `use: repair-round`, and the graph file loses 119
lines for 53.
What stopped being copied is the part that matters more than the line
count: the one-direction discipline (a review with no write tool under
permission_mode: plan, an apply with no review duty), both verdict
contracts, the apply's tool grant, its evidence-grounded gate and its
retry — identical in all four nodes, and the next correction to any of
them was a hand sweep with a miss rate. The bindings carry only what
genuinely differs per round: the focus, the reviewer, the review's
bound, the apply's extra duty, the evidence command. `cwd:` is declared
once per use and propagates to both spliced nodes.
Two prompt convergences are reviewed changes rather than equivalence
claims, and are why this graph sits in goldenTemplates and not in
migratedTemplates: round2's CLEAN/FINDINGS wording converges on
round1's, and the two apply prompts converge except for their one
differing sentence. The resolved fields are otherwise identical, empty
`review_agent`/`review_timeout` bindings included — a standard round
still runs as a plain claude session under the runner's default bound.
Three tests move with it, each for a stated reason:
- the golden set gains adr-driven-dev, so an edit to repair-round
shows up as four moved nodes in a PR diff;
- the feedback-reach sweep skips MULTI-NODE fragment files, because a
fragment only becomes a graph when it is spliced — its `nodes:`
carry {{ with.x }} where a graph carries durations and regexes. It
is swept through the templates that cite it;
- the gating-review sweep skips multi-node resolutions instead of
looking up a using id that is no longer a node, and its documented
limits now name that second blind spot.
The qualifier-clause sweep DESIGN.md publishes moves 26 → 24
declarations over the same 33 nodes: four fewer places to correct one
sentence, which is the gap that section is making a point about.
Co-Authored-By: oh-my-graph <graphs@oh-my-graph.dev>
Signed-off-by: jitokim <pigberger70@gmail.com>
* docs(adr): what implementation found, and the two docs the code moved
Fourth slice of ADR 0027, per the standing rule that code and DESIGN.md
never drift apart.
DESIGN.md's "Fragments" section states both forms and the one invariant
covering them, plus the resolution rules a graph author needs: the
namespace and why it cannot collide, rewrite-before-substitution and
what that means for a bound value, entry inheritance, cwd/worktree
propagation, exit-resolution for downstream edges and artifact tokens,
and the refusal of `rerun:` over a loop. The node-schema section points
at it from the `use:` example, and the golden list gains adr-driven-dev
with the blast radius spelled out.
docs/RUN-FEED.md states the artifact filename as a rule a consumer can
apply — the node id with '/' (and this platform's separator) replaced by
'~' — and says why '~' rather than '_': the map has to be injective, or
`a` + `b_c`, `a_b` + `c` and a hand-written `a_b_c` share one file and a
consumer is handed another node's result with nothing failing. The rule
is a no-op for every id anyone can write.
The ADR gains a "What implementation found" section recording three
things it got wrong before any code existed. The first matters most:
the conversion proof it worked out at length — backlog-batch's lane A —
is blocked by this ADR's OWN non-goal, because two of that lane's three
nodes are themselves `use:` citations and folding them in would need
`use:` inside a fragment. Converting anyway would fork e2e-verify and
review-style into a third copy each, which is the drift ADR 0013 exists
to kill. So: a lane that already uses fragments well is the hardest lane
to convert, and the ones that convert cleanly are the ones that never
reached for a fragment — which is also exactly where the measured
copy-paste is.
Co-Authored-By: oh-my-graph <graphs@oh-my-graph.dev>
Signed-off-by: jitokim <pigberger70@gmail.com>
* test(graph): a spliced node faces the graph's own validations
Fifth slice of ADR 0027, pinning the half of the design that is
deliberately not new machinery. A spliced node is an ordinary node, so
the graph-level rules judge it exactly as they judge a hand-written one,
and the error names the spliced id — which locates the using site,
because the spliced id begins with it.
Two shapes ADR 0027 lists as inherent to splicing into someone else's
graph rather than as new checks, now asserted instead of asserted-about:
an ENTRY node with `handoff: session` inherits parents whose arity the
fragment cannot know, and a gate inside a spliced feedback body is
refused post-splice by ADR 0010's rule 4.
Co-Authored-By: oh-my-graph <graphs@oh-my-graph.dev>
Signed-off-by: jitokim <pigberger70@gmail.com>
* fix(graph): three small divergences a fragment could hide behind
An empty `depends_on: []` inside a fragment node was neither an entry node
nor an internal child. Entry-hood is decided by the key's PRESENCE, so the
node inherited nothing, became a root of the citing graph, and started in
parallel with the work the using node said it came after. Say it, don't
infer it: an empty sequence is now a load error naming the node.
`resolveLoopReferences` looked up a loop trimmed and wrote it back
untrimmed, so a quoted `depends_on: [" qa"]` minted " qa/review" and died
on nodeIDPattern complaining about a shape its author never wrote. Its
sibling `namespaceNode` already had this right; the two must agree.
`fragmentFeedbackBodies` duplicates Graph.FeedbackBody, and dropped its
ancestor guard — so an `exit:` at a non-ancestor rerun target was refused
for "lying inside the feedback body" that arc does not have, at the FILE
level, which hid the true "not a proper ancestor" error entirely by never
letting the splice happen. The duplication's price is paid on the promise
that the two computations agree; this is the guard that keeps it.
cwd propagation gets the direct assertion worktree already had, rather
than only the reading through adr-driven-dev's golden.
Co-Authored-By: oh-my-graph <graphs@oh-my-graph.dev>
Signed-off-by: jitokim <pigberger70@gmail.com>
* fix(graph): the two spellings that reached in and were let through
Encapsulation was enforced on three of the four ways to reach into a
loop. The fourth is the one that actually leaks: an artifact token.
`prompt: "ship {{ artifacts.qa/review | inline }}"` names a node that
really exists after the splice and really is an ancestor of the node
quoting it, so LintPlaceholders is satisfied too and nothing anywhere
objected — at load, at lint or at run. The loop's internal output was
simply read from outside, which is the encapsulation this ADR pays a
required exit: and a widened nodeIDPattern for. It also left the host
graph laxer than the fragment, which is backwards: a fragment naming an
undeclared id in a token was already a load error.
refuseAuthoredNamespaces now walks every scalar of every authored node,
not a field list — a binding is authored text as surely as a prompt is,
and a walk cannot be outrun by a field this schema grows later. Pre-
splice, where every '/' in the document is provably one a human typed.
The second: a multi-node use: REPLACES its node with the spliced ones,
so a hand-written `qa` beside a `use:` node also called `qa` resolved to
qa, qa/impl, qa/review — all distinct, all unique, loads clean. Every
downstream depends_on: [qa] and {{ artifacts.qa }} was then rewritten to
the LOOP's exit, past a node literally named qa. Before ADR 0027 that
file was a loud duplicate-id error; after it, a working graph wired to
the wrong producer. Refused in document order, so LoadFile and LintFile
still agree on which problem comes first.
And the residue of mapping loop -> exit before the existence test: a
using node binding its OWN id passed, because its exit exists. It is now
refused on its own terms — a loop cannot be given its own output as an
input.
Co-Authored-By: oh-my-graph <graphs@oh-my-graph.dev>
Signed-off-by: jitokim <pigberger70@gmail.com>
* test(graph): a structural exclusion decided structurally
The multi-node fragment skip in the shipped-graph sweep classified with
strings.Contains(data, "\nnodes:"). A SINGLE-node fragment whose prompt
happened to contain a line beginning `nodes:` would have been dropped
silently — not far-fetched in a repo whose prompts routinely quote graph
YAML — from the very sweep that exists to catch what nobody noticed.
The exclusion is structural, so it is decided structurally: unmarshal
and test the top-level key.
Co-Authored-By: oh-my-graph <graphs@oh-my-graph.dev>
Signed-off-by: jitokim <pigberger70@gmail.com>
* fix(graph): two orders a map walk was deciding for us
fragmentFeedbackBodies ranged over its arcs map, so a fragment whose exit
lies inside two feedback bodies reported the two "lies inside the feedback
body" errors in whichever order that run drew. The package promises the
opposite one line away — refuseLoopIDCollisions walks document order for
"the deterministic first error LoadFile and LintFile must agree on", and
the multi-node table asserts issues[0] equals the LoadFile error. Those
two views load the fragment file separately, so the shuffle would not even
have been the same shuffle. Walked over ids now, deduped against the
repeat a duplicate-id fragment leaves there.
The second order is the one recordBoundReferences was called in: before
the isMulti branch, so ADR 0013's single-node form inherited a rule minted
for namespacing. A single-node use: mints no namespace and rewrites no
token — its body lands on the using node's own id — so a bound
{{ artifacts.x }} is the same thing as one typed into a plain node's
prompt, which this package leaves to the advisory sweep. It had become a
hard load error for that one spelling, undocumented in DESIGN.md (which
states the rule inside the multi-node bullet, correctly); and a node
binding its OWN id was told a loop cannot be given its own output as an
input, naming an exit and a descendant that a single-node splice has
neither of. Recorded from the multi-node branch alone now, which is where
the justification lives.
Co-Authored-By: oh-my-graph <graphs@oh-my-graph.dev>
Signed-off-by: jitokim <pigberger70@gmail.com>
* docs(graph): three comments that named more than the code does
nodeIDSegmentPattern's docstring claimed three enforcement points — the
entry graph's nodes:, a fragment file's nodes:, and the coordinator — and
has exactly one caller, judgeMultiNodeIDs. The other two test for a '/'
and leave the rest of the shape to nodeIDPattern; same net effect, so the
comment now says which check each is rather than implying a shared symbol
a reader would go looking for.
TestParse_NamespacedNodeIDAcceptedAsBackstop pointed at
TestLoadFile_AuthoredNamespaceInIDRejected, which exists nowhere but in
that line. The case is a subtest of TestLoadFile_MultiNodeLoadErrors.
And the multi-node skip in TestAGatingReviewCarriesItsRecoveryArc is
unreachable as graphs/fragments/ stands — the only multi-node fragment is
repair-round, which the review-* prefix test already skipped. Kept, since
it is what makes that test's second stated limit true rather than merely
stated, but the comment now says so instead of reading as a branch that
fires today.
Co-Authored-By: oh-my-graph <graphs@oh-my-graph.dev>
Signed-off-by: jitokim <pigberger70@gmail.com>
* fix(graph): the fifth spelling, in the one body no invariant judges
refuseAuthoredNamespaces reads the entry document, and a fragment file's
own ids are held to the declared-ids invariant — which runs only when the
file declares ids. A single-node fragment declares none: its tokens are
deliberately allowed to name the citing graph's nodes (ADR 0013), and
nothing narrowed that to non-namespaced ones. So a fragments/foo.yaml
containing {{ artifacts.round1/review | inline }} loaded, resolved, and
read a loop's internals from outside, with LintPlaceholders silent too
because round1/review really exists and really is an ancestor — the exact
property ADR 0027's finding 4 identified for the token spelling, arriving
one file over. Three artifacts stated the refusal as total, so the gap is
closed rather than the claim narrowed: the single-node branch of
loadFragmentFile refuses a namespaced token beside the check that judges
the multi-node one.
{{ feedback.qa-a }} downstream of a loop was already refused, but by
validateFeedbackPlaceholders' generic "declares no feedback edge" — about
a node the author wrote as `- id: qa-a` and is looking straight at, since
the splice replaced it. resolveLoopReferences now says what is true of it,
symmetrically with the feedback.rerun refusal beside it.
Two comments corrected rather than trusted. resolveFragments claimed
errs[0] is "the first error in document order"; resolution is three
sequential passes over one slice, so it is the first error of the earliest
pass that has one — the property LoadFile and LintFile actually share.
And spliceLoop now says that cwd+worktree on one using node reports the
contradiction once per spliced node: each spliced node genuinely carries
it, and pre-checking here would restate a rule validateWorktrees owns and
cascade a worse report onto a node stripped of its use:.
Co-Authored-By: oh-my-graph <graphs@oh-my-graph.dev>
Signed-off-by: jitokim <pigberger70@gmail.com>
* docs(adr): a node id is a key, and the grammar is the decision
The ADR described `/` and `~` as characters the design picked. That framing
invites the wrong question — "what if an author writes one anyway?" — and the
answer then has to be re-derived at every call site.
Stated as a grammar instead, in the sense a Redis key is domain-scoped: atoms
joined by a delimiter the atoms may not contain. Two properties then fall out
rather than being argued case by case. Decomposition is unique at any depth,
because no atom can hold the delimiter, so `a/b/c` is `[a b c]` and there is no
reading under which `a/b` was an atom. And the on-disk form is injective for
the same reason and not by luck, `~` being as unwritable in an atom as `/`.
An authored delimiter is a CONVENTION VIOLATION, refused where authorship
happens. That refusal is what makes the properties load-bearing rather than
hopeful, and it is why the collapse this design once seemed to risk — two ids
on one artifact file, `| inline` feeding one node's reply into another's paid
prompt — cannot happen quietly. The id never loads.
Also records that the depth cap is enforcement of this ADR's own non-goal, not
a limit of the scheme: opening nesting is `?` -> `*` and nothing else. Written
because the operator was about to add the opposite warning — that closure would
force the delimiter to be reconsidered — which is false and would have sent a
later reader looking for a problem the grammar already solved.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: jitokim <pigberger70@gmail.com>
* test(graph): prompt2 is load-bearing, and now says so
Review finding. The key looks like a typo and is not: this case's subject is
refuseNestedUse, and prompt2 only has to keep {{ with.task }} referenced so the
substitution checks stay quiet and the nesting refusal is what fires.
"Correcting" it to prompt would leave the case passing for a different reason
than the one it is named for.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: jitokim <pigberger70@gmail.com>
---------
Signed-off-by: jitokim <pigberger70@gmail.com>
Co-authored-by: oh-my-graph <graphs@oh-my-graph.dev>1 parent 989f828 commit 142ce37
25 files changed
Lines changed: 3759 additions & 211 deletions
File tree
- cmd/oh-my-graph
- docs
- adr
- graphs
- fragments
- internal
- coordinator
- graph
- testdata/golden
- handoff
- serve
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
13 | 37 | | |
14 | 38 | | |
15 | 39 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
220 | 220 | | |
221 | 221 | | |
222 | 222 | | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
223 | 229 | | |
224 | 230 | | |
225 | 231 | | |
| |||
233 | 239 | | |
234 | 240 | | |
235 | 241 | | |
236 | | - | |
| 242 | + | |
237 | 243 | | |
238 | | - | |
239 | | - | |
240 | | - | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
241 | 249 | | |
242 | 250 | | |
243 | 251 | | |
| |||
298 | 306 | | |
299 | 307 | | |
300 | 308 | | |
301 | | - | |
302 | | - | |
| 309 | + | |
| 310 | + | |
303 | 311 | | |
304 | 312 | | |
305 | 313 | | |
| |||
318 | 326 | | |
319 | 327 | | |
320 | 328 | | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
321 | 409 | | |
322 | 410 | | |
323 | | - | |
| 411 | + | |
| 412 | + | |
324 | 413 | | |
325 | 414 | | |
326 | 415 | | |
| |||
333 | 422 | | |
334 | 423 | | |
335 | 424 | | |
336 | | - | |
337 | | - | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
338 | 430 | | |
339 | 431 | | |
340 | 432 | | |
| |||
889 | 981 | | |
890 | 982 | | |
891 | 983 | | |
892 | | - | |
| 984 | + | |
893 | 985 | | |
894 | | - | |
895 | | - | |
| 986 | + | |
| 987 | + | |
| 988 | + | |
| 989 | + | |
| 990 | + | |
896 | 991 | | |
897 | 992 | | |
898 | 993 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
78 | 78 | | |
79 | 79 | | |
80 | 80 | | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
81 | 86 | | |
82 | 87 | | |
83 | 88 | | |
84 | | - | |
| 89 | + | |
85 | 90 | | |
86 | 91 | | |
87 | 92 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
173 | 173 | | |
174 | 174 | | |
175 | 175 | | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
176 | 187 | | |
177 | 188 | | |
178 | 189 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1577 | 1577 | | |
1578 | 1578 | | |
1579 | 1579 | | |
| 1580 | + | |
| 1581 | + | |
| 1582 | + | |
| 1583 | + | |
| 1584 | + | |
| 1585 | + | |
1580 | 1586 | | |
1581 | 1587 | | |
1582 | 1588 | | |
| 1589 | + | |
| 1590 | + | |
| 1591 | + | |
1583 | 1592 | | |
1584 | 1593 | | |
1585 | 1594 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
| 14 | + | |
| 15 | + | |
14 | 16 | | |
15 | 17 | | |
16 | 18 | | |
| |||
26 | 28 | | |
27 | 29 | | |
28 | 30 | | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
29 | 44 | | |
30 | 45 | | |
31 | 46 | | |
| |||
0 commit comments