Commit 75f1993
feat(linear): prefix-route multi-workspace issue lookup by team key (#273)
* feat(screenshot): preview-deploy screenshot pipeline (no stack wiring yet)
Lambda + AgentCore Browser plumbing for capturing screenshots of
preview deployments. Provider-agnostic — listens for GitHub
deployment_status events from any source (Vercel, Amplify Hosting,
Netlify, GitHub Actions custom CD).
This commit lands the handler / construct code only. Stack wiring
follows in the next commit.
* feat(screenshot): GitHubScreenshotIntegration construct + stack wiring
- New `GitHubScreenshotIntegration` construct (mirrors `LinearIntegration`):
bundles the screenshot bucket, dedup table, signing-secret placeholder,
receiver Lambda, processor Lambda, and the API Gateway route. cdk-nag
suppressions added inline (HMAC auth instead of Cognito; AgentCore
Browser sessions have no per-resource ARN; Secrets Manager rotation
is owned by GitHub).
- Wired into `agent.ts` after the LinearIntegration block. Reuses the
existing `githubTokenSecret` (the processor uses ABCA's main GitHub
token to look up which PR a deploy SHA belongs to and post the
screenshot comment — no new credential).
- Three new stack outputs: `GitHubWebhookUrl`, `GitHubWebhookSecretArn`,
`ScreenshotBucketName`.
- Bumped agent.test.ts table count from 13 to 14 to account for the
new dedup table.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(screenshot): suppress AwsSolutions-S2 on the public-read screenshot bucket
cdk-nag's S2 fires on any bucket that has `blockPublicPolicy: false`
even when the policy is intentionally permissive. Add the suppression
with the same rationale as S1/S5 — public reads are required by
GitHub Markdown renderers and Linear `imageUploadFromUrl`, and the
read grant is prefix-scoped to `screenshots/*`.
Caught when the first deploy attempt aborted at synth-time on the new
GitHubScreenshotIntegration construct.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(screenshot): private S3 bucket + CloudFront distribution
The first deploy attempt failed at CFN-execute time on the bucket
policy:
s3:PutBucketPolicy ... because public policies are prevented by
the BlockPublicPolicy setting in S3 Block Public Access.
Account-level Block Public Access is on for this AWS account, which
overrides per-bucket BPA settings. Disabling it would change the
security posture of the whole account, so route around the constraint
with the AWS-recommended pattern: private S3 + CloudFront with Origin
Access Control.
Changes:
- `ScreenshotBucket` is now `BLOCK_ALL` BPA, no public bucket policy.
Adds a `cloudfront.Distribution` whose origin is the bucket via
`S3BucketOrigin.withOriginAccessControl`. The distribution policy is
scoped to the CloudFront service principal only, so account-level
BPA accepts it.
- Processor reads `SCREENSHOT_PUBLIC_HOST` (the CloudFront domain)
instead of building an S3 URL. PR comments now embed
`https://<dist>.cloudfront.net/screenshots/...` URLs.
- New stack output `ScreenshotCloudFrontDomain`.
- Bucket-level S2/S5 suppressions removed (no longer applicable —
bucket is private). Distribution gets CFR1/CFR2/CFR3/CFR4/CFR7
suppressions with rationales.
Heads up on deploy time: CloudFront distributions take 5-15 min to
provision on first create.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(waf): exempt /v1/github/webhook from CRS like /v1/linear/webhook
The CommonRuleSet was 403'ing GitHub deployment_status webhooks before
the request reached our Lambda — the deployment payload contains
absolute Vercel preview URLs in the body, which trips GenericRFI_BODY.
Mirror the Linear webhook exemption: the GitHub webhook path is
HMAC-verified in the Lambda, parsed as strict JSON, never
interpolated into SQL/HTML, and rate-limited by the priority-3 rule.
CRS still applies to every other route.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(screenshot): read environment_url from deployment_status, not deployment
GitHub's `deployment_status` webhook puts the deployed URL on the
*status* object, not the deployment itself. The deployment object is
immutable per (sha, environment); the status changes through the
deploy lifecycle (`pending` → `success`) and carries the URL only
once the deploy finishes.
Symptom: receiver kept short-circuiting `success` events from Vercel
with `{ok: true, skipped_no_url: true}` because we read the wrong
field. Verified by inspecting the webhook delivery payload via
`gh api .../deliveries/<id> --jq .request.payload.deployment_status` —
URL was there all along.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(agentcore-browser): use ws package for SigV4-signed WebSocket handshake
Node 24's global WebSocket (from undici) does NOT support arbitrary
HTTP headers on the upgrade request — passing them as the second arg
gets silently ignored. AgentCore Browser's WSS handshake requires
SigV4-signed Authorization + X-Amz-* headers, so the connection was
opening but then getting rejected, which surfaced as an empty
`error` event ("AgentCore Browser WebSocket error: ").
Switch to the `ws` package which natively supports `options.headers`.
Also add an `unexpected-response` handler so HTTP-level handshake
failures (403, 400) surface with status codes instead of empty errors.
Smoke verified locally — the ws-based path opens cleanly against
example.com and Vercel preview URLs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(agentcore-browser): SigV4-presign WSS URL instead of signing headers
Lambda runtime returned a 403 on the WSS upgrade despite well-formed
SigV4 headers — `ws` rewrites the Host header during the upgrade
GET, which invalidates the canonical-request signature we computed
against the original Host. This works locally because Node's tooling
on macOS keeps the original Host through the handshake, but the
Lambda runtime's TLS stack normalizes differently.
Switch to query-parameter SigV4 (presigned URL): SignatureV4.presign
returns a wss://...?X-Amz-Algorithm=...&X-Amz-Signature=... URL where
the auth lives in the URL itself, so any Host-header rewriting
downstream doesn't break the signature.
Smoke verified locally — presigned URL connects cleanly to AgentCore
Browser and the screenshot pipeline runs end-to-end (6.3s, valid
PNG, captures example.com correctly).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(iam): grant bedrock-agentcore:* to the screenshot processor
The minimal IAM I shipped earlier (`StartBrowserSession`,
`StopBrowserSession`, `GetBrowserSession`, `UpdateBrowserStream`)
wasn't enough — the WSS automation-stream connect requires an
additional `ConnectBrowserAutomationStream`-flavored action that
isn't in the public CLI command list. Lambda invocations were
opening sessions cleanly but 403'ing on the WSS upgrade.
Widen to `bedrock-agentcore:*` to unblock the e2e flow. Followup:
scope back down to the specific connect action once it's documented
or surfaced via CloudTrail decoded-message-on-deny.
Smoke verified: PR #1 on isadeks/vercel-abca-linear now receives a
screenshot comment within ~7s of the deployment_status webhook.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(screenshot): also post screenshot comment to linked Linear issue
Extends the screenshot processor to find a Linear issue via the PR's
title/body and post the same image comment there.
Approach (no GSI write-back needed):
- Regex-extract Linear identifier (e.g. `ABCA-42`) from PR title/body.
These are present whether the agent put them there
(`task_description` carries the identifier) or Linear's own GitHub
integration auto-injected the back-reference on PR open.
- Scan `LinearWorkspaceRegistryTable` for `status=active` workspaces.
Per-workspace, query Linear's `issueVcsBranchSearch` (which accepts
the human-readable identifier) and accept the first exact-match
hit.
- Post the markdown image comment via the existing `postIssueComment`
helper from Phase 2.0b.
The Linear post is best-effort — if the registry table isn't wired,
the identifier doesn't extract, or the lookup misses, the GitHub PR
comment still lands. New env var `LINEAR_WORKSPACE_REGISTRY_TABLE_NAME`
is optional on the processor; the construct only sets it when the
prop is provided.
CDK: `GitHubScreenshotIntegrationProps` gains an optional
`linearWorkspaceRegistryTable`. When provided, the processor's IAM
grows: ReadData on the registry, GetSecretValue+PutSecretValue on
`bgagent-linear-oauth-*`. `agent.ts` wires
`linearIntegration.workspaceRegistryTable` into the screenshot
construct.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(screenshot): retry PR lookup to handle deploy-before-PR race
Some providers (Vercel, Netlify) post deployment_status faster than
the agent can run `gh pr create`. Retry the GitHub PR-lookup with
backoff so the screenshot finds the open PR rather than dropping the
event when the timing is reversed.
* fix(linear): silent label gate + default to 'abca' to stop unlabeled-issue comment spam
Move the trigger-label check ahead of every user-facing comment path in
the Linear webhook processor, and switch the default trigger label from
'bgagent' to 'abca'. An unlabeled issue is now a true no-op: no comment,
no reaction, no createTaskCore, no DDB writes — regardless of whether
the project is onboarded.
Why: workspace webhooks fire workspace-wide. A single un-onboarded team
in the same Linear workspace produced 47 identical "❌ project isn't
onboarded" comments on GRO-783 in 5 minutes because every Issue event
(create/update/label-change) hit the not-onboarded gate before the
label gate. With the gate order flipped, only issues that explicitly
opt in via the trigger label can ever generate user-facing feedback.
Per-project label_filter override is still respected — the project
mapping lookup now happens once, before the label gate, instead of after.
Tests: two new regression tests pin the spam scenario (unlabeled issue
in a non-onboarded project, and unlabeled issue with no projectId) to
zero side effects. Full CDK suite (89 suites / 1572 tests) passes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(screenshots): add the screenshot pipeline guide
Adds the operator walkthrough for wiring up the AgentCore-Browser
preview-deploy screenshot pipeline.
* feat(github): bgagent github webhook-info + set-webhook-secret
Mirrors the Linear webhook-info pattern so docs and onboarding don't
have to embed stack-specific URLs or copy-paste aws CLI invocations.
Two subcommands:
- `webhook-info` — read-only. Reads GitHubWebhookUrl + GitHubWebhookSecretArn
from the CFN stack outputs and prints values to paste into a GitHub
repo's webhook config (Settings → Webhooks → Add webhook). Includes
the event-type ('Deployment statuses') and content-type guidance
that operators consistently miss.
- `set-webhook-secret` — interactive PutSecretValue against the stack
output ARN. Replaces the cargo-cult `aws secretsmanager put-secret-
value` operators were copy-pasting from the screenshot setup notes.
Warns before overwriting an existing real secret (heuristic: a CDK-
seeded JSON placeholder starts with `{`; a real GitHub secret won't).
No CDK changes — both stack outputs were already there. Pure CLI add.
* docs/code(screenshots): de-Vercel-ize the screenshot pipeline
The pipeline was always provider-agnostic — it listens for GitHub
deployment_status events, which Vercel, AWS Amplify, Netlify, and
any GitHub-Actions-driven CD pipeline all post. Code comments,
inline strings, and the setup guide referenced Vercel as if it were
the only supported path; this commit aligns the surfacing with what
the code actually does.
Code:
- Linear comment body: "after the Vercel preview deploy finished"
→ "after the deploy finished" (the GitHub PR comment already
said this; just the Linear path was inconsistent)
- Webhook receiver doc-comment + envelope interface comment: drop
Vercel-only language; explain that the `environment` filter
(`SCREENSHOT_TARGET_ENVIRONMENT` env var) is configurable per-
provider, with a table of common values
- Processor PR-race comment: explain that the gap is also seen on
Netlify/Amplify, not unique to Vercel
- AgentCore Browser comment: drop Vercel-specific phrasing on
"what we don't try to be clever about"
- GitHubScreenshotIntegration construct prop docstring: explain
the per-provider env-name conventions
Docs:
- Rename VERCEL_SETUP_GUIDE.md → DEPLOY_PREVIEW_SCREENSHOTS_GUIDE.md
- Lead with a "works with any provider that posts deployment_status"
table (Vercel / Amplify / Netlify / GitHub Actions custom CD,
with "out-of-the-box?" yes/no per provider)
- Keep Vercel as the worked example since it's what we smoke-tested,
but add a "skip Steps 1-2" callout for non-Vercel providers
- New "Configuring for non-Vercel providers" section with the
SCREENSHOT_TARGET_ENVIRONMENT override pointer
- Replace 4a/4b's CFN-output spelunking with `bgagent github
webhook-info` + `bgagent github set-webhook-secret` (commands
shipped in 1c1b618)
- Troubleshooting: mention that 401 "Invalid signature" is the
set-webhook-secret-mismatch case
- Sync registration: register as DEPLOY_PREVIEW_SCREENSHOTS_GUIDE
in sync-starlight.mjs route map + the explicit mirror call;
added to astro.config.mjs sidebar after the PAK runbook
No CDK structural changes — the construct prop, env-var, and code
behaviour were already provider-agnostic. Pure surfacing fix.
* docs(screenshots): drop redundant Step 3 + condescending hardening preamble
Step 3 (repo onboarding + Linear project mapping) duplicated work
the Prerequisites section already establishes ('Linear OAuth installed
for at least one workspace'). If the user followed the Linear setup
guide, both are done. If they didn't, Step 4's smoke test fails fast
and the troubleshooting routes them back. Net: 30 lines of doc gone,
no information lost.
Renumbered Step 4 → 3 and Step 5 → 4 (and the 4a/b/c → 3a/b/c
sub-steps).
Also dropped the 'demo configuration optimizes for "look, it works"
rather than security posture' framing on the production-hardening
section. The list of followups stands on its own; the framing reads
as condescending toward someone reaching the bottom of the guide.
* docs(screenshots): drop 'followup' framing — describe gaps as current state
Public docs that say 'followup' read as commitments to do that work.
Reframe gaps as current limitations with neutral language:
- 'Production hardening (followups)' → 'Production hardening
considerations'; bullets describe what to think about, not what
ABCA promises to ship
- Netlify table row: 'followup to support pattern matching' →
'⚠ workable today only by picking one specific PR's
environment string; broader pattern matching isn't shipped'
- Vercel auth callout: 'tracked as a followup' → 'currently not
implemented'
- Non-Vercel providers table: drop 'followup #96 covers prefix
routing' reference (issue numbers don't belong in user-facing
docs)
Net: same information, no implicit roadmap commitments.
* docs(screenshots): de-Linear-ize — Linear is opt-in, not required
The screenshot pipeline only needs GitHub. Linear-side posting was
phrased as a hard requirement throughout the guide because the demo
flow happens to use Linear, but a non-Linear team gets a perfectly
useful integration: screenshots land on GitHub PRs, the Linear
lookup silently no-ops.
Reframings:
- Lead-in: 'on both the open GitHub PR AND the linked Linear issue'
→ 'on the open GitHub PR. If you also have Linear configured,
the same screenshot is posted to the linked Linear issue as a
bonus.' Plus a note on the gating (LinearWorkspaceRegistryTable
having active rows is what flips the Linear path on).
- 'How it works': step 4 (Linear post) marked optional with the
silent-skip behaviour spelled out
- Architecture comment: 'GitHub PR comment + Linear issue comment'
→ '... (+ Linear issue comment if linked)'
- Prerequisites: Linear OAuth marked optional with rationale
- Smoke test: rewritten as PR-driven by default ('open any PR on
the configured repo'), with Linear-driven path as a follow-on
paragraph ('If you also have Linear configured...')
- Troubleshooting: 'Linear is best-effort' → 'opt-in and best-
effort', explicit note that skipping is normal without Linear
* feat(screenshot): hide URL behind 'preview link' label in comments
GitHub PR comment now reads 'From [preview link](url)' and Linear
comment reads '[Preview link](url)' instead of pasting the bare URL.
Cleaner visual when the same comment is posted on both surfaces.
* docs(screenshots): add USER_GUIDE / COST_MODEL / ROADMAP coverage
Closes the doc gaps from the screenshot feature followup list:
- USER_GUIDE.md: new 'Preview-deploy screenshots (optional)' subsection
under Notifications, points at DEPLOY_PREVIEW_SCREENSHOTS_GUIDE.md.
- COST_MODEL.md: 'Optional: deploy-preview screenshots' table covering
AgentCore Browser session, Lambda processor, S3, CloudFront line items
(~$0.01 per screenshot, dominated by Browser session time).
- ROADMAP.md: marks the feature shipped under Notification plane with a
one-line description of the trigger model and post-deploy latency.
Mirrors regenerated via docs/scripts/sync-starlight.mjs.
* docs(linear): clarify teammate-onboarding handshake
The 'Inviting teammates' section was missing the prerequisite that the
teammate needs their own ABCA account (Cognito user + configured CLI)
before they can redeem a Linear invite code. New flow walks through:
Admin: invite-user (Cognito) → invite-user <slug> (Linear)
Teammate: configure --from-bundle → login → linear link <code>
with cross-references to USER_GUIDE.md's 'Joining an existing
deployment' for the Cognito-side details. Also corrects the stale
'auto-links the person running the wizard' claim — setup now offers
an inline picker (opt-in by admin), not an automatic mapping.
* fix(github-cli): de-Vercel-ize webhook-info / set-webhook-secret strings
Last batch of stale 'Vercel' framing in CLI command output, missed in
the original de-Vercel-ize sweep. Provider-agnostic now: webhook-info
header reads 'preview-deploy screenshot pipeline', the closing note
lists Vercel/Amplify/Netlify/GitHub Actions as example providers, and
the smoke-test instruction says 'push to a PR-attached branch' rather
than 'trigger a Vercel preview deploy'.
No behaviour change; pure copy.
* fix(github-cli): replace template literal with single quotes (eslint mutation)
The local build's eslint --fix step rewrote a no-interpolation
template literal to single quotes; CI's 'Fail build on mutation'
guard caught that the mutation wasn't committed. Apply the fix.
* fix(screenshot): krokoko PR-241 review — scope IAM + cosmetic Vercel mention
Closes #94 (the existing 'scope IAM down from bedrock-agentcore:*'
followup task).
Addresses krokoko's PR #241 review:
1. (BLOCKING per review #1) IAM action wildcard — narrow
bedrock-agentcore:* to the three calls the screenshot processor
actually makes:
- StartBrowserSession (control plane, public CLI command)
- StopBrowserSession (control plane, public CLI command)
- ConnectBrowserAutomationStream (SigV4-presigned WSS dial; not
in the public CLI list but verified live against the deployed
dev stack — IAM accepts the action name)
Resource wildcard remains because AgentCore Browser sessions are
ephemeral with no stable ARN; the IAM5 suppression on the construct
already documents that.
Previous behaviour granted every AgentCore action surface (memory,
runtime, gateway, identity, code-interpreter) the screenshot path
doesn't use. Tightening to the call set leaves a precise audit
surface; if a future API change needs another action, IAM denies
with the action name in CloudTrail and we add it explicitly.
2. (NIT per review #7) Stale 'Vercel' wording on ScreenshotBucketName
CfnOutput description, plus an adjacent comment in agent.ts that
said 'Vercel-style preview deploys'. Both replaced with
provider-agnostic phrasing — the pipeline listens for any provider
that posts deployment_status (Vercel, Amplify, Netlify, GitHub
Actions custom CD).
No behavioural change in either fix.
* feat(linear): prefix-route multi-workspace issue lookup by team key
Closes #96.
The screenshot processor's findLinearIssueByIdentifier scanned every
active workspace until one returned a hit. Cheap for 1-2 workspaces, but
wasteful for stacks onboarding several Linear workspaces.
Cache each workspace's team keys (e.g. ['ABCA', 'PLAT']) on the registry
row at install time. The lookup now prefix-matches the identifier's team
key (ABCA-42 -> ABCA) and queries that workspace first; iterates the rest
only on miss or when team_keys is absent (legacy rows / stale cache).
- linear-issue-lookup.ts: split into prefix-match + fallback-iterate
- cli/commands/linear.ts: queryLinearTeamKeys helper, called from setup
and add-workspace; team_keys persisted on registry row
- DynamoDB schemaless — no CDK schema change. Legacy rows back-fill on
next 'bgagent linear setup' / 'add-workspace' re-run.
* test(linear): cover queryLinearTeamKeys to clear coverage gate
The new helper added in 059450e (PR #273) brought the global statement
+ line coverage in cli/ to 67.74% — under the 68% floor enforced by
the coverage-thresholds contract. CI was failing on this.
Export queryLinearTeamKeys for testing and add 5 unit tests covering:
- happy path: uppercase + dedup + sort
- empty / non-string keys filtered
- non-2xx response → []
- fetch throws → []
- malformed GraphQL response → []
Coverage: 67.74% → 68.09% (clears 68% floor). 299/299 tests pass.
* chore(cli): apply eslint formatting to queryLinearTeamKeys tests
CI's mutation guard rejected the previous commit's compact JSON
fixture style; eslint --fix expands the nested data/teams/nodes
literal across multiple lines. No behavior change; 26/26 tests
still pass.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: bgagent <bgagent@noreply.github.com>
Co-authored-by: Alain Krok <alkrok@amazon.com>1 parent 71a8c31 commit 75f1993
3 files changed
Lines changed: 182 additions & 10 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
73 | 73 | | |
74 | 74 | | |
75 | 75 | | |
76 | | - | |
77 | | - | |
78 | | - | |
| 76 | + | |
79 | 77 | | |
80 | | - | |
81 | | - | |
82 | | - | |
83 | | - | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
84 | 87 | | |
85 | 88 | | |
86 | 89 | | |
| |||
90 | 93 | | |
91 | 94 | | |
92 | 95 | | |
93 | | - | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
94 | 101 | | |
95 | 102 | | |
96 | 103 | | |
| |||
101 | 108 | | |
102 | 109 | | |
103 | 110 | | |
| 111 | + | |
104 | 112 | | |
105 | 113 | | |
106 | 114 | | |
| |||
114 | 122 | | |
115 | 123 | | |
116 | 124 | | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
117 | 150 | | |
| 151 | + | |
118 | 152 | | |
119 | 153 | | |
120 | 154 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
627 | 627 | | |
628 | 628 | | |
629 | 629 | | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
630 | 634 | | |
631 | 635 | | |
632 | 636 | | |
| |||
637 | 641 | | |
638 | 642 | | |
639 | 643 | | |
| 644 | + | |
640 | 645 | | |
641 | 646 | | |
642 | | - | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
643 | 652 | | |
644 | 653 | | |
645 | 654 | | |
| |||
979 | 988 | | |
980 | 989 | | |
981 | 990 | | |
| 991 | + | |
| 992 | + | |
982 | 993 | | |
983 | 994 | | |
984 | 995 | | |
| |||
989 | 1000 | | |
990 | 1001 | | |
991 | 1002 | | |
| 1003 | + | |
992 | 1004 | | |
993 | 1005 | | |
994 | | - | |
| 1006 | + | |
| 1007 | + | |
| 1008 | + | |
| 1009 | + | |
| 1010 | + | |
995 | 1011 | | |
996 | 1012 | | |
997 | 1013 | | |
| |||
1638 | 1654 | | |
1639 | 1655 | | |
1640 | 1656 | | |
| 1657 | + | |
| 1658 | + | |
| 1659 | + | |
| 1660 | + | |
| 1661 | + | |
| 1662 | + | |
| 1663 | + | |
| 1664 | + | |
| 1665 | + | |
| 1666 | + | |
| 1667 | + | |
| 1668 | + | |
| 1669 | + | |
| 1670 | + | |
| 1671 | + | |
| 1672 | + | |
| 1673 | + | |
| 1674 | + | |
| 1675 | + | |
| 1676 | + | |
| 1677 | + | |
| 1678 | + | |
| 1679 | + | |
| 1680 | + | |
| 1681 | + | |
| 1682 | + | |
| 1683 | + | |
| 1684 | + | |
| 1685 | + | |
| 1686 | + | |
| 1687 | + | |
| 1688 | + | |
| 1689 | + | |
| 1690 | + | |
1641 | 1691 | | |
1642 | 1692 | | |
1643 | 1693 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| 27 | + | |
27 | 28 | | |
28 | 29 | | |
29 | 30 | | |
| |||
372 | 373 | | |
373 | 374 | | |
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 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
0 commit comments