Repository navigation
Commit 7911485
ci(half-state-patrol): a declared
Fixes #20798
Clause-②: no. CI wiring, no published contract, per ruling `5905998989`.
This executes decision A of the decision card #20793 (ruling
`5905998989`; the maintainer's answer, verbatim: "A: opt-in action input
(Recommended)"), inside the maintainer's order on #18471.
objectstack-ai/objectui#11174 is waiting on this card and switches onto
the action once this lands, with the opt-in on and a sha pinned at that
time.
## What changes
**`.github/actions/half-state-patrol/action.yml`: one new declared
input, `anchor-optional`, default `'false'`.**
- **Locate the patrol sources** judges the value once, on every event,
and publishes it as `anchor_optional`. It accepts the six YAML 1.2
core-schema boolean spellings that `core.getBooleanInput` takes. Any
other value is `::error::` + exit 1.
- **Resolve the anchor issue** gains `id: anchor` and one new block in
front of the existing ones. An empty (or whitespace-only) `anchor-issue`
with the opt-in on writes `configured=false`, prints a `::notice::`, and
exits 0. The existing empty-anchor block that follows it (`::error::` +
exit 1, `action.yml:313-315` on `main`) is byte-identical. The
non-numeric check is unchanged and runs in both modes. A usable anchor
writes `configured=true`.
- **Update the pinned anchor issue**: its guard gains `&&
steps.anchor.outputs.configured == 'true'`, so the opt-in never reaches
the PATCH. H1 holds: without this guard, the opt-in PATCHes issue 0 (see
the ablation below).
- **Publish the rendered body to the run summary** adds one line, and
only on the explicit `configured == 'false'` reading. The findings go to
the run summary as before, since this step is `if: always()`.
- The header gains a section, "An empty anchor: loud by default,
summary-only by declaration". The action's top-level `description` names
the summary-only delivery.
- For every valid value, the pull_request path is unchanged: both anchor
steps are still skipped there. The one exception is the value check
above, which is a judgment call stated under Acceptance notes.
`scripts/pm/check-half-states.mjs` is untouched.
**`.github/workflows/half-state-patrol.yml` (objectstack's own caller):
prose only, and the default is kept.** The sibling-install section now
names the opt-in for a board that has no anchor by decision. The
anchor-resolution comment says this caller keeps `anchor-optional` at
its loud default. The `with:` block is unchanged.
## The input as declared
```yaml
anchor-optional:
description: >-
`true` declares that an EMPTY `anchor-issue` is this caller's supported configuration: that one case becomes a `::notice::` and a passing run, with the findings delivered to the run summary only. It exists for one consumer, objectui, whose maintainer declined an anchor there (#20793, decision A). The default, `false`, stays LOUD -- an empty anchor fails the run -- because without the declaration an empty value reads the same as a caller that lost its variable, and objectstack's own caller must go red when that happens. It never widens: a non-numeric `anchor-issue` fails either way, and a value other than true or false is refused on every run, pull_request included.
required: false
default: 'false'
```
## The three modes: a runnable probe of the action's own shell
There is no action or workflow test harness for this action in the repo.
H2 is falsified: `git grep -n "anchor-issue\|closed-floor"` finds only
the action itself and the caller's prose. No test, doc or script reads
the input list. So this is measured with a probe,
`probe-anchor-modes.py`, whose full source is at the end of this body.
How it works:
- It loads `action.yml` and runs every step's real `run:` body in order.
Each body is template-expanded and run the way the runner's `shell:
bash` runs it (`bash --noprofile --norc -eo pipefail`).
- It evaluates each `if:` with a small GitHub-expression evaluator. As
the runner does, it wraps the expression in `success() && (...)` unless
it names a status function.
- It uses real `GITHUB_OUTPUT`, `GITHUB_STEP_SUMMARY` and `RUNNER_TEMP`
files.
- It stubs two steps that this diff does not change: the sweep (a stub
report plus the scenario's exit code) and the github-script PATCH. For
the PATCH it prints the issue number that `Number(ANCHOR_ISSUE)` would
target, computed by node.
Run at `c35b098b1`: `python3 probe-anchor-modes.py
.github/actions/half-state-patrol/action.yml .` (exit 0).
| scenario | event | anchor-issue | anchor-optional | Locate | Resolve |
Update (the PATCH) | summary line | job |
|---|---|---|---|---|---|---|---|---|
| off+empty | schedule | `''` | unset | exit 0 | exit 1 `::error::` |
skipped | none | FAILURE |
| off-explicit+empty | schedule | `''` | `false` | exit 0 | exit 1
`::error::` | skipped | none | FAILURE |
| **on+empty** | schedule | `''` | `true` | exit 0 | exit 0 `::notice::`
| skipped | "No anchor issue configured ..." | **success** |
| on+whitespace | workflow_dispatch | `' '` | `true` | exit 0 | exit 0
`::notice::` | skipped | "No anchor issue configured ..." | success |
| off+non-numeric | schedule | `'abc'` | unset | exit 0 | exit 1
`::error::` | skipped | none | FAILURE |
| on+non-numeric | schedule | `'abc'` | `true` | exit 0 | exit 1
`::error::` | skipped | none | FAILURE |
| off+number | schedule | `'9857'` | unset | exit 0 | exit 0 | runs,
PATCHes issue 9857 | none | success |
| on+number | schedule | `'9857'` | `true` | exit 0 | exit 0 | runs,
PATCHes issue 9857 | none | success |
| pr+off+empty | pull_request | `''` | unset | exit 0 | skipped |
skipped | "Anchor write skipped ..." | success |
| pr+on+empty | pull_request | `''` | `true` | exit 0 | skipped |
skipped | "Anchor write skipped ..." | success |
| bad-optional | schedule | `''` | `yes` | exit 1 `::error::` | skipped
| skipped | none | FAILURE |
| pr+bad-optional | pull_request | `''` | `yes` | exit 1 `::error::` |
skipped | skipped | "Anchor write skipped ..." | FAILURE |
| on+empty, sweep exit 3 | schedule | `''` | `true` | exit 0 | exit 0
`::notice::` | skipped | "No anchor issue configured ..." | FAILURE (the
final "sweep could not run" step) |
| off+empty, sweep exit 3 | schedule | `''` | unset | exit 0 | exit 1
`::error::` | skipped | none | FAILURE |
The rows the card requires, quoted from that run:
```text
=== off+empty | event=schedule anchor-issue='' anchor-optional=UNSET(default 'false') sweep-exit=0
- Locate the patrol sources exit 0 outputs {'anchor_optional': 'false'}
- Check the workspace serves the swept board exit 0
- Run the live sweep exit 0 outputs {'exit_code': '0'}
(stub) check-half-states exited 0
- Resolve the anchor issue exit 1
::error::No anchor issue configured for objectstack-ai/objectui. The sweep RAN (see the run summary) but has nowhere to land. Open a `tracking`-labeled anchor issue in this repo and pass its number as the 'anchor-issue' input of the half-state-patrol action.
- Update the pinned anchor issue skipped
- Publish the rendered body to the run summary exit 0
- Fail the run if the sweep could not run skipped
summary: ### Half-state patrol — sweep exit 0
JOB: FAILURE
=== on+empty | event=schedule anchor-issue='' anchor-optional='true' sweep-exit=0
- Locate the patrol sources exit 0 outputs {'anchor_optional': 'true'}
- Check the workspace serves the swept board exit 0
- Run the live sweep exit 0 outputs {'exit_code': '0'}
(stub) check-half-states exited 0
- Resolve the anchor issue exit 0 outputs {'configured': 'false'}
::notice::No anchor issue configured for objectstack-ai/objectui, which is a supported configuration for this caller: it declares 'anchor-optional: true' on the half-state-patrol action. The sweep RAN and its rendered body is in this run's summary; nothing was written to any issue, and the missing anchor does not fail this run. To deliver findings to a pinned issue instead, open a `tracking`-labeled anchor issue in this repo and pass its number as the 'anchor-issue' input.
- Update the pinned anchor issue skipped
- Publish the rendered body to the run summary exit 0
- Fail the run if the sweep could not run skipped
summary: ### Half-state patrol — sweep exit 0
summary: _No anchor issue configured (`anchor-optional` is on and `anchor-issue` is empty): this summary is the ONLY delivery of the findings below — it notifies nobody, by accepted trade. Pass a `tracking` issue's number as `anchor-issue` to have them land on a pinned card instead._
JOB: success
=== off+non-numeric | event=schedule anchor-issue='abc' anchor-optional=UNSET(default 'false') sweep-exit=0
- Locate the patrol sources exit 0 outputs {'anchor_optional': 'false'}
- Check the workspace serves the swept board exit 0
- Run the live sweep exit 0 outputs {'exit_code': '0'}
(stub) check-half-states exited 0
- Resolve the anchor issue exit 1
::error::The 'anchor-issue' input is 'abc', which is not an issue number.
- Update the pinned anchor issue skipped
- Publish the rendered body to the run summary exit 0
- Fail the run if the sweep could not run skipped
summary: ### Half-state patrol — sweep exit 0
JOB: FAILURE
=== on+non-numeric | event=schedule anchor-issue='abc' anchor-optional='true' sweep-exit=0
- Locate the patrol sources exit 0 outputs {'anchor_optional': 'true'}
- Check the workspace serves the swept board exit 0
- Run the live sweep exit 0 outputs {'exit_code': '0'}
(stub) check-half-states exited 0
- Resolve the anchor issue exit 1
::error::The 'anchor-issue' input is 'abc', which is not an issue number.
- Update the pinned anchor issue skipped
- Publish the rendered body to the run summary exit 0
- Fail the run if the sweep could not run skipped
summary: ### Half-state patrol — sweep exit 0
JOB: FAILURE
```
**The default mode is unchanged, measured against `main`.** The same
probe was run on the base blob
(`41dcf1188:.github/actions/half-state-patrol/action.yml`, blob
`fc455f1ef`) and on this head, for off+empty, off+non-numeric,
off+number, pr+off+empty and off+empty with sweep exit 3. The outputs
were compared after dropping the `outputs {...}` annotation, since the
head's steps now also publish `anchor_optional` and `configured`. Across
all 66 lines, each scenario differs in one line only: the scenario
header, which reads `UNSET(default '-')` on base (no such input) and
`UNSET(default 'false')` on head. Every step line, annotation, summary
line and job verdict is identical.
**Ablation of the Update guard (H1).** This was run on a scratch copy of
the committed `action.yml`, never on the tree. The copy had the guard's
`&& steps.anchor.outputs.configured == 'true'` removed; `grep -c` found
that text 0 times in the copy and 1 time in HEAD. The probe then read:
```text
=== on+empty | event=schedule anchor-issue='' anchor-optional='true' sweep-exit=0
- Update the pinned anchor issue exit 0
(stub) actions/github-script@v9 would PATCH issue #0
=== on+whitespace | event=workflow_dispatch anchor-issue=' ' anchor-optional='true' sweep-exit=0
- Update the pinned anchor issue exit 0
(stub) actions/github-script@v9 would PATCH issue #0
```
So the opt-in needs the guard. On a real runner that PATCH answers 404
and turns the run red, which is the state this card exists to remove.
## Gates (local, at `c35b098b1`)
- The gate list comes from `node scripts/pm/dispatch-gates.mjs
--commands --repo objectstack-ai/objectstack`: 39 commands, derived from
merge base `41dcf1188` over the 2 changed paths.
- All 39 exit 0 on the final run. `node scripts/pm/dispatch-gates.mjs
--ran ran.list` reconciles them as `39 derived, 39 run, 0 NOT-MEASURED,
0 UNRUN`, with an exit code recorded for every one. `pnpm
check:pm-dispatch-gates` is longer than the container's foreground cap,
so it ran detached, as its header prescribes, under a foreground `tail
--pid` wait. It printed `✓ check:pm-dispatch-gates --self-test: the exit
contract holds in all three directions.` and `✓ dispatch-gates
self-test: 1976 cases pass.` (1018.6s), with 0 `✗` lines.
- `pnpm install` for the root package only (`--filter
@objectstack/spec-monorepo`). 15 gates first exited 3 (`PREREQUISITE NOT
MET`, the `yaml` dependency) and are green on the re-run after it.
- Workflow-valued families: `check-half-states.mjs` and
`check-issue-citations.mjs --census` are NOT MEASURED locally. Their
argv carries runner-only values. This PR's own `half-state-patrol.yml`
run exercises the PR path on a real runner, because the action's
directory is a trigger path. That run includes the new value check at
its default.
- `actionlint`: NOT MEASURED. It is not on PATH and not in
`node_modules/.bin`. `check:workflow-status-functions` and
`check:workflow-step-name-quoting` are green.
- Changeset: none, because nothing publishes. Both changed paths are
owned only by the private root package `@objectstack/spec-monorepo`.
`pnpm ls -r` finds 81 workspace packages, 69 public, and none of them
owns a changed path. `check-empty-changeset.mjs --base 41dcf11` is
green (0 changesets added). `check-changeset-no-major.mjs --base
41dcf11 --event EVENT` is green, reading this body's `Clause-②: no`
line. The disposition is `skip-changeset`. This dispatch did not allow
label writes, so the seat applies it, and `Check Changeset` reads red
until then.
## Acceptance notes
- **One judgment call, stated rather than buried.** The value check on
`anchor-optional` sits in "Locate the patrol sources", so it runs on
pull_request runs too.
- For every valid value, the pull_request path is unchanged. The
pr+off+empty and pr+on+empty rows match base, and both anchor steps are
still skipped.
- Only a misspelt opt-in behaves differently: it is refused on the PR
that adds it, instead of passing that PR's run and first failing on the
next scheduled run.
- If the seat reads "the pull_request path stays unchanged" as ruling
this out, the alternative is a mechanical move of the `case` into the
Resolve step (non-PR only).
- **The notice differs from objectui#8740's wording in one clause.** It
says "the missing anchor does not fail this run" rather than "this run
is NOT a failure". With the opt-in on, a sweep that could not run still
fails the run in its final step (probe row "on+empty, sweep exit 3").
The notice names the setting (`anchor-optional: true`) where objectui's
named its repository variable.
- **Not exercised on a real runner: the non-PR modes.** A
`workflow_dispatch` of this branch would PATCH objectstack's live
anchor. The first live reading of on+empty will be objectui's switch
(objectstack-ai/objectui#11174).
- A composite action does not refuse an input it does not declare. A
caller that pins a sha from before this change and passes
`anchor-optional` gets only the runner's "Unexpected input" warning, and
the empty anchor still fails loudly. That is safe, but objectui's switch
has to pin a sha that contains this change.
## The probe (`probe-anchor-modes.py`)
```python
#!/usr/bin/env python3
# probe-anchor-modes.py ACTION_YML REPO_ROOT [scenario ...]
#
# Runs the half-state-patrol composite action's REAL `run:` bodies, in order, under
# the runner's step rules, once per anchor scenario:
# - each `run:` is template-expanded (${{ ... }}) and executed as the runner's
# `shell: bash` does: bash --noprofile --norc -eo pipefail FILE;
# - each `if:` is evaluated by a small GitHub-expression evaluator, wrapped in
# success() && (...) unless it names a status function, as the runner does;
# - GITHUB_OUTPUT / GITHUB_STEP_SUMMARY / RUNNER_TEMP are real files.
# Two steps are STUBBED, both unchanged by the diff under test: the sweep (writes a
# report and publishes the scenario's exit code) and the github-script PATCH
# (prints the issue number Number(ANCHOR_ISSUE) would PATCH, computed by node).
import os, re, subprocess, sys, tempfile, yaml
ACTION, ROOT = sys.argv[1], os.path.abspath(sys.argv[2])
ONLY = sys.argv[3:]
action = yaml.safe_load(open(ACTION))
STEPS = action['runs']['steps']
DEFAULTS = {k: str(v.get('default', '')) for k, v in action['inputs'].items()}
TOK = re.compile(r"\s*(\|\||&&|==|!=|\(|\)|'(?:[^']|'')*'|[A-Za-z_][\w.-]*\(\)|[A-Za-z_][\w.-]*)")
def truthy(v):
return v not in ('', None, False, 0)
def text(v):
return 'true' if v is True else 'false' if v is False else '' if v is None else str(v)
def evaluate(expr, ctx):
s, toks, i = expr.strip(), [], 0
while i != len(s):
m = TOK.match(s, i)
if not m:
raise ValueError('cannot tokenize: ' + s[i:])
toks.append(m.group(1)); i = m.end()
pos = [0]
peek = lambda: toks[pos[0]] if pos[0] != len(toks) else None
def take():
pos[0] += 1
return toks[pos[0] - 1]
def primary():
t = take()
if t == '(':
v = orx(); take(); return v
if t.startswith("'"):
return t[1:-1].replace("''", "'")
if t.endswith('()'):
return {'always()': True, 'success()': ctx['job_ok'], 'failure()': not ctx['job_ok']}[t]
v = ctx
for part in t.split('.'):
v = v.get(part, '') if isinstance(v, dict) else ''
return v
def cmp():
a = primary()
if peek() in ('==', '!='):
op, b = take(), primary()
eq = text(a).lower() == text(b).lower() # GitHub compares strings case-insensitively
return eq if op == '==' else not eq
return a
def andx():
v = cmp()
while peek() == '&&':
take(); r = cmp(); v = r if truthy(v) else v
return v
def orx():
v = andx()
while peek() == '||':
take(); r = andx(); v = v if truthy(v) else r
return v
return orx()
def expand(s, ctx):
return re.sub(r'\$\{\{(.*?)\}\}', lambda m: text(evaluate(m.group(1), ctx)), str(s), flags=re.S)
def should_run(step, ctx):
cond = step.get('if')
if cond is None:
return ctx['job_ok']
c = str(cond).strip()
if c.startswith('${{'):
c = c[3:-2]
if not re.search(r'\b(always|success|failure|cancelled)\(\)', c):
c = 'success() && (' + c + ')'
return truthy(evaluate(c, ctx))
def scenario(name, event, anchor, optional, sweep_exit=0):
tmp = tempfile.mkdtemp(prefix='hsp-probe-')
summary = os.path.join(tmp, 'summary.md'); open(summary, 'w').close()
inputs = dict(DEFAULTS, **{'github-token': 'probe-token', 'anchor-issue': anchor})
if optional is not None:
inputs['anchor-optional'] = optional
ctx = {'job_ok': True, 'inputs': inputs, 'steps': {}, 'github': {
'event_name': event, 'repository': 'objectstack-ai/objectui', 'workspace': ROOT,
'action_path': os.path.join(ROOT, '.github/actions/half-state-patrol'),
'run_id': '1', 'server_url': 'https://github.com', 'sha': '0' * 40}}
print('=== %s | event=%s anchor-issue=%r anchor-optional=%s sweep-exit=%d'
% (name, event, anchor, 'UNSET(default %r)' % DEFAULTS.get('anchor-optional', '-') if optional is None else repr(optional), sweep_exit))
for step in STEPS:
sid, label = step.get('id', ''), step['name']
if not should_run(step, ctx):
print(' - %-48s skipped' % label)
ctx['steps'][sid] = {'outputs': {}}
continue
env = dict(os.environ, RUNNER_TEMP=tmp, GITHUB_STEP_SUMMARY=summary,
GITHUB_OUTPUT=os.path.join(tmp, 'out-' + (sid or 'x')), GITHUB_REPOSITORY='objectstack-ai/objectui')
open(env['GITHUB_OUTPUT'], 'w').close()
for k, v in (step.get('env') or {}).items():
env[k] = expand(v, ctx)
if sid == 'sweep': # STUB: the sweep itself is not under test
open(os.path.join(tmp, 'report.md'), 'w').write('os-half-state-sweep (probe stub report)\n')
open(os.path.join(tmp, 'report.err'), 'w').write('')
open(env['GITHUB_OUTPUT'], 'w').write('exit_code=%d\n' % sweep_exit)
code, out = 0, '(stub) check-half-states exited %d' % sweep_exit
elif 'uses' in step: # STUB: the github-script PATCH
n = subprocess.run(['node', '-p', 'Number(process.env.ANCHOR_ISSUE)'], env=env, capture_output=True, text=True).stdout.strip()
code, out = 0, '(stub) %s would PATCH issue #%s' % (step['uses'], n)
else:
script = os.path.join(tmp, 'step.sh')
open(script, 'w').write(expand(step['run'], ctx))
cwd = expand(step.get('working-directory', ROOT), ctx)
p = subprocess.run(['bash', '--noprofile', '--norc', '-eo', 'pipefail', script],
cwd=cwd, env=env, capture_output=True, text=True)
code, out = p.returncode, (p.stdout + p.stderr).strip()
outputs = dict(l.split('=', 1) for l in open(env['GITHUB_OUTPUT']).read().splitlines() if '=' in l)
ctx['steps'][sid] = {'outputs': outputs}
if code != 0:
ctx['job_ok'] = False
shown = {k: v for k, v in outputs.items() if k != 'root'}
print(' - %-48s exit %d%s' % (label, code, (' outputs ' + repr(shown)) if shown else ''))
for line in out.splitlines():
if line.startswith(('::', '(stub)', 'anchor:')):
print(' ' + line)
for line in open(summary).read().splitlines():
if line.startswith(('###', '_')):
print(' summary: ' + line)
print(' JOB: %s' % ('success' if ctx['job_ok'] else 'FAILURE'))
print()
SCENARIOS = [
('off+empty', 'schedule', '', None),
('off-explicit+empty', 'schedule', '', 'false'),
('on+empty', 'schedule', '', 'true'),
('on+whitespace', 'workflow_dispatch', ' ', 'true'),
('off+non-numeric', 'schedule', 'abc', None),
('on+non-numeric', 'schedule', 'abc', 'true'),
('off+number', 'schedule', '9857', None),
('on+number', 'schedule', '9857', 'true'),
('pr+off+empty', 'pull_request', '', None),
('pr+on+empty', 'pull_request', '', 'true'),
('bad-optional', 'schedule', '', 'yes'),
('pr+bad-optional', 'pull_request', '', 'yes'),
('on+empty+sweep-died', 'schedule', '', 'true', 3),
('off+empty+sweep-died', 'schedule', '', None, 3),
]
for s in SCENARIOS:
if not ONLY or s[0] in ONLY:
scenario(*s)
```
---
_Generated by [Claude
Code](https://claude.ai/code/session_01TdiauJaVCHuj45EzZGUxHh)_
Co-authored-by: Claude <noreply@anthropic.com>anchor-optional input lets a board with no anchor issue run summary-only (default off) (#20812)1 parent 261c529 commit 7911485
2 files changed
Lines changed: 81 additions & 7 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
121 | 121 | | |
122 | 122 | | |
123 | 123 | | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
124 | 135 | | |
125 | 136 | | |
126 | 137 | | |
127 | 138 | | |
128 | | - | |
129 | | - | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
130 | 143 | | |
131 | 144 | | |
132 | 145 | | |
| |||
142 | 155 | | |
143 | 156 | | |
144 | 157 | | |
145 | | - | |
| 158 | + | |
| 159 | + | |
146 | 160 | | |
147 | 161 | | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
148 | 176 | | |
149 | 177 | | |
150 | 178 | | |
| |||
169 | 197 | | |
170 | 198 | | |
171 | 199 | | |
| 200 | + | |
172 | 201 | | |
173 | 202 | | |
174 | 203 | | |
| |||
201 | 230 | | |
202 | 231 | | |
203 | 232 | | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
204 | 251 | | |
205 | 252 | | |
206 | 253 | | |
| |||
304 | 351 | | |
305 | 352 | | |
306 | 353 | | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
307 | 362 | | |
308 | 363 | | |
309 | 364 | | |
310 | 365 | | |
311 | 366 | | |
| 367 | + | |
312 | 368 | | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
313 | 374 | | |
314 | 375 | | |
315 | 376 | | |
316 | 377 | | |
317 | 378 | | |
318 | 379 | | |
319 | 380 | | |
| 381 | + | |
320 | 382 | | |
321 | 383 | | |
322 | 384 | | |
323 | | - | |
324 | | - | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
325 | 389 | | |
326 | 390 | | |
327 | 391 | | |
| |||
401 | 465 | | |
402 | 466 | | |
403 | 467 | | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
404 | 474 | | |
405 | 475 | | |
406 | 476 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
116 | 116 | | |
117 | 117 | | |
118 | 118 | | |
119 | | - | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
120 | 123 | | |
121 | 124 | | |
122 | 125 | | |
| |||
279 | 282 | | |
280 | 283 | | |
281 | 284 | | |
282 | | - | |
| 285 | + | |
| 286 | + | |
283 | 287 | | |
284 | 288 | | |
285 | 289 | | |
| |||
0 commit comments