Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package forgejo-runner for openSUSE:Factory 
checked in at 2026-01-05 14:54:02
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/forgejo-runner (Old)
 and      /work/SRC/openSUSE:Factory/.forgejo-runner.new.1928 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "forgejo-runner"

Mon Jan  5 14:54:02 2026 rev:38 rq:1325318 version:12.4.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/forgejo-runner/forgejo-runner.changes    
2025-12-25 19:58:01.112151935 +0100
+++ /work/SRC/openSUSE:Factory/.forgejo-runner.new.1928/forgejo-runner.changes  
2026-01-05 14:56:53.347581470 +0100
@@ -1,0 +2,11 @@
+Sun Jan  4 20:24:48 UTC 2026 - Richard Rahl <[email protected]>
+
+- Update to version 12.4.0:
+  * feat: include all integration tests in make integration-test
+  * feat: request up to capacity jobs from Forgejo in one API call
+  * fix: remove cmd and ports from container schema
+  * fix: evaluate jobs.<job_id>.container.volumes
+  * fix: re-parsing incomplete jobs must use on.workflow_call.inputs, not
+    global inputs
+
+-------------------------------------------------------------------

Old:
----
  forgejo-runner-12.3.1.obscpio

New:
----
  forgejo-runner-12.4.0.obscpio

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ forgejo-runner.spec ++++++
--- /var/tmp/diff_new_pack.9rCpS4/_old  2026-01-05 14:56:56.299704132 +0100
+++ /var/tmp/diff_new_pack.9rCpS4/_new  2026-01-05 14:56:56.303704299 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package forgejo-runner
 #
-# Copyright (c) 2025 SUSE LLC and contributors
+# Copyright (c) 2026 SUSE LLC and contributors
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,7 +18,7 @@
 
 %define services %{name}.service
 Name:           forgejo-runner
-Version:        12.3.1
+Version:        12.4.0
 Release:        0
 Summary:        Daemon that connects to a Forgejo instance and runs CI jobs
 License:        GPL-3.0-or-later
@@ -28,7 +28,7 @@
 Source2:        %{name}.service
 BuildRequires:  fish
 BuildRequires:  zsh
-BuildRequires:  golang(API) >= 1.23
+BuildRequires:  golang(API) >= 1.25
 BuildRequires:  pkgconfig(bash-completion)
 BuildRequires:  pkgconfig(systemd)
 Requires:       git-core

++++++ _service ++++++
--- /var/tmp/diff_new_pack.9rCpS4/_old  2026-01-05 14:56:56.339705795 +0100
+++ /var/tmp/diff_new_pack.9rCpS4/_new  2026-01-05 14:56:56.343705961 +0100
@@ -2,7 +2,7 @@
   <service name="obs_scm" mode="manual">
     <param name="url">https://code.forgejo.org/forgejo/runner</param>
     <param name="scm">git</param>
-    <param name="revision">refs/tags/v12.3.1</param>
+    <param name="revision">refs/tags/v12.4.0</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="changesgenerate">disable</param>
     <param name="versionrewrite-pattern">v(.*)</param>

++++++ forgejo-runner-12.3.1.obscpio -> forgejo-runner-12.4.0.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/forgejo-runner-12.3.1/.forgejo/workflows/test.yml 
new/forgejo-runner-12.4.0/.forgejo/workflows/test.yml
--- old/forgejo-runner-12.3.1/.forgejo/workflows/test.yml       2025-12-24 
17:06:29.000000000 +0100
+++ new/forgejo-runner-12.4.0/.forgejo/workflows/test.yml       2025-12-31 
15:18:43.000000000 +0100
@@ -153,12 +153,11 @@
           # gcc -- for `go test -race`
           apt-get install -qq -y \
             qemu-user-static binfmt-support \
-            gcc
+            gcc make
 
       - name: integration test
         run: |
-          go test -race ./act/container
-          go test -race -timeout 30m ./act/runner/...
+          make act-integration-test
         env:
           TEST_MUST_SUPPORT_MIXED_ARCH: true
 
@@ -205,7 +204,7 @@
 
       - run: apt-get install -y -qq gcc # required for `-race`
 
-      - run: make integration-test
+      - run: make runner-integration-test
 
   validate-mocks:
     name: validate mocks
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/forgejo-runner-12.3.1/Makefile 
new/forgejo-runner-12.4.0/Makefile
--- old/forgejo-runner-12.3.1/Makefile  2025-12-24 17:06:29.000000000 +0100
+++ new/forgejo-runner-12.4.0/Makefile  2025-12-31 15:18:43.000000000 +0100
@@ -110,9 +110,18 @@
        $(GO) test -race -short ./act/container
        $(GO) test -race ./act/artifactcache/... ./act/workflowpattern/... 
./act/filecollector/... ./act/common/... ./act/jobparser ./act/model 
./act/exprparser ./act/schema
 
-integration-test:
+.PHONY: integration-test
+integration-test: runner-integration-test act-integration-test
+
+.PHONY: runner-integration-test
+runner-integration-test:
        @$(GO) test -race -v ./internal/app/run/...
 
+.PHONY: act-integration-test
+act-integration-test:
+       @$(GO) test -race ./act/container
+       @$(GO) test -race -v -timeout 30m ./act/runner/...
+
 .PHONY: vet
 vet:
        @echo "Running go vet..."
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/forgejo-runner-12.3.1/act/container/docker_run.go 
new/forgejo-runner-12.4.0/act/container/docker_run.go
--- old/forgejo-runner-12.3.1/act/container/docker_run.go       2025-12-24 
17:06:29.000000000 +0100
+++ new/forgejo-runner-12.4.0/act/container/docker_run.go       2025-12-31 
15:18:43.000000000 +0100
@@ -13,6 +13,7 @@
        "path/filepath"
        "regexp"
        "runtime"
+       "slices"
        "strconv"
        "strings"
        "time"
@@ -579,6 +580,18 @@
                config.Hostname = jobConfig.Config.Hostname
        }
 
+       if len(jobConfig.Config.User) > 0 {
+               logger.Debugf("--user %v", jobConfig.Config.User)
+               config.User = jobConfig.Config.User
+       }
+
+       for _, group := range jobConfig.HostConfig.GroupAdd {
+               if !slices.Contains(hostConfig.GroupAdd, group) {
+                       logger.Debugf("--group-add %v", group)
+                       hostConfig.GroupAdd = append(hostConfig.GroupAdd, group)
+               }
+       }
+
        return config, hostConfig, nil
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/forgejo-runner-12.3.1/act/container/docker_run_test.go 
new/forgejo-runner-12.4.0/act/container/docker_run_test.go
--- old/forgejo-runner-12.3.1/act/container/docker_run_test.go  2025-12-24 
17:06:29.000000000 +0100
+++ new/forgejo-runner-12.4.0/act/container/docker_run_test.go  2025-12-31 
15:18:43.000000000 +0100
@@ -375,6 +375,16 @@
                        config:     &container.Config{},
                        hostConfig: &container.HostConfig{},
                },
+               {
+                       name:    "MergeUserAndGroupAdd",
+                       options: "--user asdf --user root --group-add group1 
--group-add wheel --group-add system --group-add wheel --group-add group1",
+                       config: &container.Config{
+                               User: "root",
+                       },
+                       hostConfig: &container.HostConfig{
+                               GroupAdd: []string{"group1", "wheel", "system"},
+                       },
+               },
        } {
                t.Run(testCase.name, func(t *testing.T) {
                        cr := &containerReference{
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/forgejo-runner-12.3.1/act/jobparser/jobparser.go 
new/forgejo-runner-12.4.0/act/jobparser/jobparser.go
--- old/forgejo-runner-12.3.1/act/jobparser/jobparser.go        2025-12-24 
17:06:29.000000000 +0100
+++ new/forgejo-runner-12.4.0/act/jobparser/jobparser.go        2025-12-31 
15:18:43.000000000 +0100
@@ -63,6 +63,17 @@
                pc.parentUniqueID = workflow.Metadata.WorkflowCallID
        }
 
+       // `pc.inputs` are the inputs for the job that should be used whenever 
`${{ inputs... }}` appears in the job
+       // definition. However, if `content` represents a reusable workflow 
job, then any reference to `${{ inputs... }}`
+       // should actually come from `on.workflow_call.inputs`. Normally this 
is handled in `expandReusableWorkflow` by
+       // replacing the inputs in the `ParseOption` array, but, we could also 
be re-parsing a reusable workflow that was
+       // incomplete in which case `pc.inputs` will be the global inputs and 
incorrect for evaluation. That case needs to
+       // be detected and overridden:
+       if workflow.Metadata.WorkflowCallParent != "" {
+               // This workflow has parent metadata, so that means it is a 
child workflow job; replace `pc.inputs`.
+               pc.inputs = getWorkflowCallInputDefaults(origin)
+       }
+
        results := map[string]*JobResult{}
        for id, job := range origin.Jobs {
                results[id] = &JobResult{
@@ -903,3 +914,25 @@
 
        return hex.EncodeToString(h.Sum(nil))
 }
+
+func getWorkflowCallInputDefaults(job *model.Workflow) map[string]any {
+       workflowCallConfig := job.WorkflowCallConfig()
+       if workflowCallConfig == nil {
+               return nil
+       }
+
+       overrideInputs := map[string]any{}
+       for k, input := range workflowCallConfig.Inputs {
+               overrideInputs[k] = input.Default
+               var value any
+               if value == nil {
+                       _ = input.Default.Decode(&value)
+               }
+               if input.Type == "boolean" {
+                       overrideInputs[k] = value == "true" || value == true
+               } else {
+                       overrideInputs[k] = value
+               }
+       }
+       return overrideInputs
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/forgejo-runner-12.3.1/act/jobparser/jobparser_test.go 
new/forgejo-runner-12.4.0/act/jobparser/jobparser_test.go
--- old/forgejo-runner-12.3.1/act/jobparser/jobparser_test.go   2025-12-24 
17:06:29.000000000 +0100
+++ new/forgejo-runner-12.4.0/act/jobparser/jobparser_test.go   2025-12-31 
15:18:43.000000000 +0100
@@ -477,6 +477,37 @@
                                }),
                        },
                },
+               // `expand_reusable_incomplete6` tests expanding an incomplete 
reusable workflow within a parent reusable
+               // workflow, and being able to reference the inputs that were 
defined from the parent workflow and stored in
+               // `on.workflow_call.inputs` as default values.  In particular, 
the `inputs` provided to the jobparser shouldn't
+               // be used since those will always be Forgejo's perspective of 
the outer-most workflow's inputs.
+               {
+                       name:                           
"expand_reusable_incomplete6",
+                       reparsingSingleWorkflow:        true,
+                       expectingInvalidWorkflowOutput: true,
+                       options: []ParseOption{
+                               WithWorkflowNeeds([]string{"define-runs-on"}),
+                               WithJobOutputs(map[string]map[string]string{
+                                       "define-runs-on": {
+                                               "runners": "nixos-25.11",
+                                       },
+                               }),
+                               WithInputs(map[string]any{
+                                       // These inputs should all be 
overridden but are provided to ensure they don't appear in the output
+                                       "example-boolean-required": false,
+                                       "example-number-required":  456,
+                                       "example-string-required":  "no thanks",
+                               }),
+                               SupportIncompleteRunsOn(),
+                               ExpandLocalReusableWorkflows(func(job *Job, 
path string) ([]byte, error) {
+                                       if path == 
"./.forgejo/workflows/expand_reusable_incomplete6_reusable.yml" {
+                                               content := ReadTestdata(t, 
"expand_reusable_incomplete6_reusable.yaml", true)
+                                               return content, nil
+                                       }
+                                       return nil, fmt.Errorf("unexpected 
local path: %q", path)
+                               }),
+                       },
+               },
        }
        for _, tt := range tests {
                t.Run(tt.name, func(t *testing.T) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/forgejo-runner-12.3.1/act/jobparser/testdata/expand_reusable_incomplete6.in.yaml
 
new/forgejo-runner-12.4.0/act/jobparser/testdata/expand_reusable_incomplete6.in.yaml
--- 
old/forgejo-runner-12.3.1/act/jobparser/testdata/expand_reusable_incomplete6.in.yaml
        1970-01-01 01:00:00.000000000 +0100
+++ 
new/forgejo-runner-12.4.0/act/jobparser/testdata/expand_reusable_incomplete6.in.yaml
        2025-12-31 15:18:43.000000000 +0100
@@ -0,0 +1,30 @@
+"on":
+  workflow_call:
+    inputs:
+      example-boolean-required:
+        default: true
+        type: boolean
+      example-number-required:
+        default: 123
+        type: number
+      example-string-required:
+        default: reusable-1
+        type: string
+name: test
+jobs:
+  reusable:
+    name: reusable (incomplete matrix)
+    needs: define-runs-on
+    runs-on: []
+    uses: ./.forgejo/workflows/expand_reusable_incomplete6_reusable.yml
+    with:
+      input1: ${{ inputs.example-boolean-required }}
+      input2: ${{ inputs.example-number-required }}
+      input3: ${{ inputs.example-string-required }}
+      input4: ${{ needs.define-runs-on.outputs.runners }}
+incomplete_with: true
+incomplete_with_needs:
+  job: define-runs-on
+  output: runners
+__metadata:
+  workflow_call_parent: 
93e6933d7bffb610a7aeecf39163ff8c6ec563c2f4b84ea80d9144de72de713b
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/forgejo-runner-12.3.1/act/jobparser/testdata/expand_reusable_incomplete6.out.yaml
 
new/forgejo-runner-12.4.0/act/jobparser/testdata/expand_reusable_incomplete6.out.yaml
--- 
old/forgejo-runner-12.3.1/act/jobparser/testdata/expand_reusable_incomplete6.out.yaml
       1970-01-01 01:00:00.000000000 +0100
+++ 
new/forgejo-runner-12.4.0/act/jobparser/testdata/expand_reusable_incomplete6.out.yaml
       2025-12-31 15:18:43.000000000 +0100
@@ -0,0 +1,56 @@
+name: test
+"on":
+  workflow_call:
+    inputs:
+      example-boolean-required:
+        default: true
+        type: boolean
+      example-number-required:
+        default: 123
+        type: number
+      example-string-required:
+        default: reusable-1
+        type: string
+jobs:
+  reusable:
+    name: reusable
+    needs:
+      - define-runs-on
+      - reusable.job1
+    runs-on: []
+    if: false
+__metadata:
+  workflow_call_inputs:
+    input1: true
+    input2: 123
+    input3: reusable-1
+    input4: nixos-25.11
+  workflow_call_id: 
93e6933d7beeb610a7aeecf39163ff8c6ec563c2f4b84ea80d9144de72de713b
+  workflow_call_parent: 
93e6933d7bffb610a7aeecf39163ff8c6ec563c2f4b84ea80d9144de72de713b
+---
+name: test
+"on":
+  workflow_call:
+    inputs:
+      input1:
+        default: true
+        type: boolean
+      input2:
+        default: 123
+        type: number
+      input3:
+        default: reusable-1
+        type: string
+      input4:
+        default: nixos-25.11
+        type: string
+jobs:
+  reusable.job1:
+    name: job1
+    needs:
+      - define-runs-on
+    runs-on: nixos-25.11
+    steps:
+      - run: echo "Woohoo!"
+__metadata:
+  workflow_call_parent: 
93e6933d7beeb610a7aeecf39163ff8c6ec563c2f4b84ea80d9144de72de713b
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/forgejo-runner-12.3.1/act/jobparser/testdata/expand_reusable_incomplete6_reusable.yaml
 
new/forgejo-runner-12.4.0/act/jobparser/testdata/expand_reusable_incomplete6_reusable.yaml
--- 
old/forgejo-runner-12.3.1/act/jobparser/testdata/expand_reusable_incomplete6_reusable.yaml
  1970-01-01 01:00:00.000000000 +0100
+++ 
new/forgejo-runner-12.4.0/act/jobparser/testdata/expand_reusable_incomplete6_reusable.yaml
  2025-12-31 15:18:43.000000000 +0100
@@ -0,0 +1,17 @@
+on:
+  workflow_call:
+    inputs:
+      input1:
+        type: boolean
+      input2:
+        type: number
+      input3:
+        type: string
+      input4:
+        type: string
+
+jobs:
+  job1:
+    runs-on: ${{ inputs.input4 }}
+    steps:
+      - run: echo "Woohoo!"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/forgejo-runner-12.3.1/act/runner/run_context.go 
new/forgejo-runner-12.4.0/act/runner/run_context.go
--- old/forgejo-runner-12.3.1/act/runner/run_context.go 2025-12-24 
17:06:29.000000000 +0100
+++ new/forgejo-runner-12.4.0/act/runner/run_context.go 2025-12-31 
15:18:43.000000000 +0100
@@ -154,7 +154,12 @@
 
        if job := rc.Run.Job(); job != nil {
                if container := job.Container(); container != nil {
-                       for _, v := range container.Volumes {
+                       interpolatedVolumes := make([]string, 0, 
len(container.Volumes))
+                       for _, volume := range container.Volumes {
+                               interpolatedVolumes = 
append(interpolatedVolumes, rc.ExprEval.Interpolate(ctx, volume))
+                       }
+
+                       for _, v := range interpolatedVolumes {
                                if !strings.Contains(v, ":") || 
filepath.IsAbs(v) {
                                        // Bind anonymous volume or host file.
                                        binds = append(binds, v)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/forgejo-runner-12.3.1/act/runner/run_context_test.go 
new/forgejo-runner-12.4.0/act/runner/run_context_test.go
--- old/forgejo-runner-12.3.1/act/runner/run_context_test.go    2025-12-24 
17:06:29.000000000 +0100
+++ new/forgejo-runner-12.4.0/act/runner/run_context_test.go    2025-12-31 
15:18:43.000000000 +0100
@@ -268,14 +268,19 @@
                                rc := &RunContext{
                                        Name: "TestRCName",
                                        Run: &model.Run{
+                                               JobID: "test",
                                                Workflow: &model.Workflow{
                                                        Name: 
"TestWorkflowName",
+                                                       Jobs: 
map[string]*model.Job{
+                                                               "test": {},
+                                                       },
                                                },
                                        },
                                        Config: &Config{
                                                BindWorkdir: false,
                                        },
                                }
+                               rc.ExprEval = 
rc.NewExpressionEvaluator(t.Context())
                                rc.Run.JobID = "job1"
                                rc.Run.Workflow.Jobs = 
map[string]*model.Job{"job1": job}
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/forgejo-runner-12.3.1/act/runner/runner_test.go 
new/forgejo-runner-12.4.0/act/runner/runner_test.go
--- old/forgejo-runner-12.3.1/act/runner/runner_test.go 2025-12-24 
17:06:29.000000000 +0100
+++ new/forgejo-runner-12.4.0/act/runner/runner_test.go 2025-12-31 
15:18:43.000000000 +0100
@@ -6,6 +6,7 @@
        "fmt"
        "io"
        "os"
+       "path"
        "path/filepath"
        "runtime"
        "strings"
@@ -199,6 +200,7 @@
                Matrix:                cfg.Matrix,
                JobLoggerLevel:        cfg.JobLoggerLevel,
                ContainerDaemonSocket: os.Getenv("DOCKER_HOST"),
+               ValidVolumes:          cfg.ValidVolumes,
        }
        if cfg.GitHubInstance != "" {
                runnerConfig.GitHubInstance = cfg.GitHubInstance
@@ -289,11 +291,6 @@
                {workdir, "fail", "push", "Job 'build' failed", platforms, 
secrets},
                {workdir, "runs-on", "push", "", platforms, secrets},
                {workdir, "checkout", "push", "", platforms, secrets},
-               {workdir, "job-container", "push", "", platforms, secrets},
-               {workdir, "job-container-non-root", "push", "", platforms, 
secrets},
-               {workdir, "job-container-invalid-credentials", "push", "failed 
to handle credentials: failed to interpolate container.credentials.password", 
platforms, secrets},
-               {workdir, "job-container-env-reference", "push", "", platforms, 
map[string]string{"ALPINE_TAG": "3.22"}},
-               {workdir, "container-hostname", "push", "", platforms, secrets},
                {workdir, "remote-action-docker", "push", "", platforms, 
secrets},
                {workdir, "remote-action-js", "push", "", platforms, secrets},
                // {workdir, "remote-action-js-node-user", "push", "", 
platforms, secrets}, // Test if this works with non root container
@@ -342,12 +339,24 @@
                {workdir, "stepsummary", "push", "", platforms, secrets},
                {workdir, "tool-cache", "push", "", platforms, secrets},
 
+               // job container
+               {workdir, "job-container", "push", "", platforms, secrets},
+               {workdir, "job-container-env", "push", "", platforms, secrets},
+               {workdir, "job-container-image", "push", "", platforms, 
map[string]string{"ALPINE_TAG": "3.22"}},
+               {workdir, "job-container-invalid-credentials", "push", "failed 
to handle credentials: failed to interpolate container.credentials.password", 
platforms, secrets},
+               {workdir, "job-container-non-root", "push", "", platforms, 
secrets},
+               {workdir, "job-container-options", "push", "", platforms, 
secrets},
+               {workdir, "job-container-options-group-add", "push", "", 
platforms, secrets},
+               {workdir, "job-container-options-user", "push", "", platforms, 
secrets},
+
                // services
                {workdir, "services", "push", "", platforms, secrets},
                {workdir, "services-with-container", "push", "", platforms, 
secrets},
                {workdir, "mysql-service-container-with-health-check", "push", 
"", platforms, secrets},
                {workdir, "mysql-service-container-premature-terminate", 
"push", "service [maindb]", platforms, secrets},
                {workdir, "services-empty-image", "push", "", platforms, 
secrets},
+               {workdir, "services-options-group-add", "push", "", platforms, 
secrets},
+               {workdir, "services-options-user", "push", "", platforms, 
secrets},
                {workdir, "services-context-expression", "push", "", platforms, 
secrets},
        }
 
@@ -813,3 +822,26 @@
                })
        }
 }
+
+func TestRunner_JobContainerVolumes(t *testing.T) {
+       if testing.Short() {
+               t.Skip("skipping integration test")
+       }
+       skip.If(t, runtime.GOOS != "linux") // Windows and macOS cannot run 
linux docker container natively
+
+       tempDir := t.TempDir()
+       volumePath := path.Join(tempDir, "file.txt")
+       err := os.WriteFile(volumePath, []byte("hello"), 0o644)
+       require.NoError(t, err)
+
+       jobFile := TestJobFileInfo{
+               workdir:      workdir,
+               workflowPath: "job-container-volumes",
+               eventName:    "push",
+               errorMessage: "",
+               platforms:    platforms,
+       }
+
+       config := &Config{Env: map[string]string{"volume_path": volumePath}, 
ValidVolumes: []string{volumePath}}
+       jobFile.runTest(t.Context(), t, config)
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/forgejo-runner-12.3.1/act/runner/testdata/container-hostname/push.yml 
new/forgejo-runner-12.4.0/act/runner/testdata/container-hostname/push.yml
--- old/forgejo-runner-12.3.1/act/runner/testdata/container-hostname/push.yml   
2025-12-24 17:06:29.000000000 +0100
+++ new/forgejo-runner-12.4.0/act/runner/testdata/container-hostname/push.yml   
1970-01-01 01:00:00.000000000 +0100
@@ -1,26 +0,0 @@
-name: container-hostname
-on: push
-
-defaults:
-  run:
-    shell: bash
-
-jobs:
-  with-hostname:
-    runs-on: ubuntu-latest
-    container:
-      image: code.forgejo.org/oci/node:22-bookworm
-      options: "--hostname my.host.local"
-    steps:
-      - run: |
-          echo "HOST: $(uname -n)"
-          [[ "$(uname -n)" == "my.host.local" ]]
-
-  default-hostname:
-    runs-on: ubuntu-latest
-    container:
-      image: code.forgejo.org/oci/node:22-bookworm
-    steps:
-      - run: |
-          echo "HOST: $(uname -n)"
-          [[ "$(uname -n)" != "my.host.local" ]]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/forgejo-runner-12.3.1/act/runner/testdata/job-container-env/push.yml 
new/forgejo-runner-12.4.0/act/runner/testdata/job-container-env/push.yml
--- old/forgejo-runner-12.3.1/act/runner/testdata/job-container-env/push.yml    
1970-01-01 01:00:00.000000000 +0100
+++ new/forgejo-runner-12.4.0/act/runner/testdata/job-container-env/push.yml    
2025-12-31 15:18:43.000000000 +0100
@@ -0,0 +1,15 @@
+on:
+  push:
+env:
+  name: "Alice"
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    container:
+      image: code.forgejo.org/oci/node:22-bookworm
+      env:
+        greeting: "Hello ${{ env.name }}"
+    steps:
+      - run: |
+          echo "env.greeting=${{ env.greeting }}"
+          [[ "${{ env.greeting }}" = "Hello Alice" ]] || exit 1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/forgejo-runner-12.3.1/act/runner/testdata/job-container-env-reference/push.yml
 
new/forgejo-runner-12.4.0/act/runner/testdata/job-container-env-reference/push.yml
--- 
old/forgejo-runner-12.3.1/act/runner/testdata/job-container-env-reference/push.yml
  2025-12-24 17:06:29.000000000 +0100
+++ 
new/forgejo-runner-12.4.0/act/runner/testdata/job-container-env-reference/push.yml
  1970-01-01 01:00:00.000000000 +0100
@@ -1,21 +0,0 @@
-name: job-container
-on: push
-
-env:
-  ATdirect: 3.22
-  ATvar: ${{ secrets.ALPINE_TAG }}
-
-jobs:
-  test:
-    runs-on: ubuntu-latest
-    container:
-      image: code.forgejo.org/oci/alpine:${{ env.ATdirect }}
-    steps:
-      - run: cat /etc/os-release
-      - run: echo ${{ env.ATdirect }}
-      - run: echo ${{ env.ATvar }}
-  test2:
-    runs-on: ubuntu-latest
-    container: code.forgejo.org/oci/alpine:${{ env.ATvar }}
-    steps:
-      - run: cat /etc/os-release
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/forgejo-runner-12.3.1/act/runner/testdata/job-container-image/push.yml 
new/forgejo-runner-12.4.0/act/runner/testdata/job-container-image/push.yml
--- old/forgejo-runner-12.3.1/act/runner/testdata/job-container-image/push.yml  
1970-01-01 01:00:00.000000000 +0100
+++ new/forgejo-runner-12.4.0/act/runner/testdata/job-container-image/push.yml  
2025-12-31 15:18:43.000000000 +0100
@@ -0,0 +1,21 @@
+name: job-container
+on: push
+
+env:
+  ATdirect: 3.22
+  ATvar: ${{ secrets.ALPINE_TAG }}
+
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    container:
+      image: code.forgejo.org/oci/alpine:${{ env.ATdirect }}
+    steps:
+      - run: cat /etc/os-release
+      - run: echo ${{ env.ATdirect }}
+      - run: echo ${{ env.ATvar }}
+  test2:
+    runs-on: ubuntu-latest
+    container: code.forgejo.org/oci/alpine:${{ env.ATvar }}
+    steps:
+      - run: cat /etc/os-release
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/forgejo-runner-12.3.1/act/runner/testdata/job-container-options/push.yml 
new/forgejo-runner-12.4.0/act/runner/testdata/job-container-options/push.yml
--- 
old/forgejo-runner-12.3.1/act/runner/testdata/job-container-options/push.yml    
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/forgejo-runner-12.4.0/act/runner/testdata/job-container-options/push.yml    
    2025-12-31 15:18:43.000000000 +0100
@@ -0,0 +1,28 @@
+name: container-hostname
+on: push
+
+defaults:
+  run:
+    shell: bash
+env:
+  hostname: "my.host.local"
+
+jobs:
+  with-hostname:
+    runs-on: ubuntu-latest
+    container:
+      image: code.forgejo.org/oci/node:22-bookworm
+      options: "--hostname ${{ env.hostname }}"
+    steps:
+      - run: |
+          echo "HOST: $(uname -n)"
+          [[ "$(uname -n)" == "my.host.local" ]]
+
+  default-hostname:
+    runs-on: ubuntu-latest
+    container:
+      image: code.forgejo.org/oci/node:22-bookworm
+    steps:
+      - run: |
+          echo "HOST: $(uname -n)"
+          [[ "$(uname -n)" != "my.host.local" ]]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/forgejo-runner-12.3.1/act/runner/testdata/job-container-options-group-add/push.yml
 
new/forgejo-runner-12.4.0/act/runner/testdata/job-container-options-group-add/push.yml
--- 
old/forgejo-runner-12.3.1/act/runner/testdata/job-container-options-group-add/push.yml
      1970-01-01 01:00:00.000000000 +0100
+++ 
new/forgejo-runner-12.4.0/act/runner/testdata/job-container-options-group-add/push.yml
      2025-12-31 15:18:43.000000000 +0100
@@ -0,0 +1,17 @@
+name: job-container-options-group-add
+on: push
+
+jobs:
+  # test job groups
+  job-with-default-groups:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Check default groups
+        run: groups | grep "^root$" || exit 1
+  job-with-additional-groups:
+    runs-on: ubuntu-latest
+    container:
+      options: --group-add users --group-add daemon
+    steps:
+      - name: Check added groups
+        run: groups | grep "^root daemon users$" || exit 1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/forgejo-runner-12.3.1/act/runner/testdata/job-container-options-user/push.yml
 
new/forgejo-runner-12.4.0/act/runner/testdata/job-container-options-user/push.yml
--- 
old/forgejo-runner-12.3.1/act/runner/testdata/job-container-options-user/push.yml
   1970-01-01 01:00:00.000000000 +0100
+++ 
new/forgejo-runner-12.4.0/act/runner/testdata/job-container-options-user/push.yml
   2025-12-31 15:18:43.000000000 +0100
@@ -0,0 +1,17 @@
+name: job-container-options-user
+on: push
+
+jobs:
+  # test job user
+  job-as-default-user:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Check default user is root
+        run: whoami | grep "^root$" || exit 1
+  job-as-nobody:
+    runs-on: ubuntu-latest
+    container:
+      options: --user nobody
+    steps:
+      - name: Check user is now nobody
+        run: whoami | grep "^nobody$" || exit 1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/forgejo-runner-12.3.1/act/runner/testdata/job-container-volumes/push.yml 
new/forgejo-runner-12.4.0/act/runner/testdata/job-container-volumes/push.yml
--- 
old/forgejo-runner-12.3.1/act/runner/testdata/job-container-volumes/push.yml    
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/forgejo-runner-12.4.0/act/runner/testdata/job-container-volumes/push.yml    
    2025-12-31 15:18:43.000000000 +0100
@@ -0,0 +1,13 @@
+on:
+  push:
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    container:
+      image: code.forgejo.org/oci/node:22-bookworm
+      volumes:
+        - "${{ env.volume_path }}:/mnt/file.txt"
+    steps:
+      - run: |
+          [[ -f "/mnt/file.txt" ]] || exit 1
+          [[ "$(cat /mnt/file.txt)" = "hello" ]] || exit 1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/forgejo-runner-12.3.1/act/runner/testdata/services-options-group-add/push.yml
 
new/forgejo-runner-12.4.0/act/runner/testdata/services-options-group-add/push.yml
--- 
old/forgejo-runner-12.3.1/act/runner/testdata/services-options-group-add/push.yml
   1970-01-01 01:00:00.000000000 +0100
+++ 
new/forgejo-runner-12.4.0/act/runner/testdata/services-options-group-add/push.yml
   2025-12-31 15:18:43.000000000 +0100
@@ -0,0 +1,26 @@
+name: services-options-group-add
+on: push
+
+jobs:
+  # test service groups
+  service-with-default-groups:
+    runs-on: ubuntu-latest
+    services:
+      nginx:
+        image: 'nginx:latest'
+        # this runs the webserver iff the condition is ok. We then look for 
the webserver in the job steps
+        cmd: ["sh", "-c", "(groups | grep \"^root$\") && nginx -g 'daemon 
off;'"]
+    steps:
+      - name: Test whether webserver is reachable
+        run: curl -sfI http://nginx:80
+  service-with-additional-groups:
+    runs-on: ubuntu-latest
+    services:
+      nginx:
+        options: --group-add users --group-add daemon
+        image: 'nginx:latest'
+        # this runs the webserver iff the condition is ok. We then look for 
the webserver in the job steps
+        cmd: ["sh", "-c", "(groups | grep \"^root daemon users$\") && nginx -g 
'daemon off;'"]
+    steps:
+      - name: Test whether webserver is reachable
+        run: curl -sfI http://nginx:80
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/forgejo-runner-12.3.1/act/runner/testdata/services-options-user/push.yml 
new/forgejo-runner-12.4.0/act/runner/testdata/services-options-user/push.yml
--- 
old/forgejo-runner-12.3.1/act/runner/testdata/services-options-user/push.yml    
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/forgejo-runner-12.4.0/act/runner/testdata/services-options-user/push.yml    
    2025-12-31 15:18:43.000000000 +0100
@@ -0,0 +1,25 @@
+name: services-options-user
+on: push
+
+jobs:
+  # test service user
+  service-as-default-user:
+    runs-on: ubuntu-latest
+    services:
+      nginx:
+        image: 'nginx:latest'
+        # this runs the webserver iff the condition is ok. We then look for 
the webserver in the job steps
+        cmd: ["sh", "-c", "(whoami | grep \"^root$\") && nginx -g 'daemon 
off;'"]
+    steps:
+      - name: Test whether webserver is reachable
+        run: curl -sfI http://nginx:80
+  service-as-nobody:
+    runs-on: ubuntu-latest
+    services:
+      nginx:
+        options: --user nobody
+        image: 'nginx:latest'
+        # Here we try to run the webserver but expect a failure since nobody 
cannot bind to port 80.
+    steps:
+      - name: Test whether webserver is reachable
+        run: bash -c '! curl -sfI http://nginx:80'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/forgejo-runner-12.3.1/act/schema/schema_test.go 
new/forgejo-runner-12.4.0/act/schema/schema_test.go
--- old/forgejo-runner-12.3.1/act/schema/schema_test.go 2025-12-24 
17:06:29.000000000 +0100
+++ new/forgejo-runner-12.4.0/act/schema/schema_test.go 2025-12-31 
15:18:43.000000000 +0100
@@ -359,6 +359,70 @@
        assert.NoError(t, err)
 }
 
+func TestSchemaContainer(t *testing.T) {
+       var node yaml.Node
+       err := yaml.Unmarshal([]byte(`
+on:
+  push:
+jobs:
+  container-schema:
+    runs-on: ubuntu-latest
+    container:
+      image: alpine:3.20
+      credentials:
+        username: 'root'
+        password: 'admin1234'
+      env:
+        SOME_VARIABLE: contents
+      options: "--hostname alpine"
+      volumes:
+        - /srv/example:/srv/example
+    steps:
+      - run: echo "OK"
+`), &node)
+       require.NoError(t, err)
+       n := &Node{
+               Definition: "workflow-root",
+               Schema:     GetWorkflowSchema(),
+       }
+       require.NoError(t, n.UnmarshalYAML(&node))
+}
+
+func TestSchemaServices(t *testing.T) {
+       var node yaml.Node
+       err := yaml.Unmarshal([]byte(`
+on:
+  push:
+jobs:
+  service-schema:
+    runs-on: ubuntu-latest
+    services:
+      pgsql:
+        image: code.forgejo.org/oci/postgres:15
+        credentials:
+          username: 'root'
+          password: 'admin1234'
+        env:
+          POSTGRES_DB: test
+          POSTGRES_PASSWORD: postgres
+        ports:
+          - 5432:5432
+        options: "--hostname pgsql"
+        cmd:
+          - /some/command
+        volumes:
+          - /srv/example:/srv/example
+    steps:
+      - run: echo "OK"
+`), &node)
+       require.NoError(t, err)
+       n := &Node{
+               Definition: "workflow-root",
+               Schema:     GetWorkflowSchema(),
+       }
+       require.NoError(t, n.UnmarshalYAML(&node))
+}
+
 func TestSchemaServicesContextExpressions(t *testing.T) {
        var node yaml.Node
        err := yaml.Unmarshal([]byte(`
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/forgejo-runner-12.3.1/act/schema/workflow_schema.json 
new/forgejo-runner-12.4.0/act/schema/workflow_schema.json
--- old/forgejo-runner-12.3.1/act/schema/workflow_schema.json   2025-12-24 
17:06:29.000000000 +0100
+++ new/forgejo-runner-12.4.0/act/schema/workflow_schema.json   2025-12-31 
15:18:43.000000000 +0100
@@ -2024,15 +2024,7 @@
             "type": "non-empty-string",
             "description": "Use `jobs.<job_id>.container.options` to configure 
additional Docker container resource options."
           },
-          "cmd": {
-            "type": "sequence-of-non-empty-string",
-            "description": "Use `jobs.<job_id>.container.cmd` to specify the 
command to run instead of the default."
-          },
           "env": "container-env",
-          "ports": {
-            "type": "sequence-of-non-empty-string",
-            "description": "Use `jobs.<job_id>.container.ports` to set an 
array of ports to expose on the container."
-          },
           "volumes": {
             "type": "sequence-of-non-empty-string",
             "description": "Use `jobs.<job_id>.container.volumes` to set an 
array of volumes for the container to use. You can use volumes to share data 
between services or other steps in a job. You can specify named Docker volumes, 
anonymous Docker volumes, or bind mounts on the host."
@@ -2041,35 +2033,80 @@
         }
       }
     },
+    "container-registry-credentials": {
+      "description": "If the image's container registry requires 
authentication to pull the image, you can use 
`jobs.<job_id>.container.credentials` to set a map of the username and 
password. The credentials are the same values that you would provide to the 
`docker login` command.",
+      "context": ["forge", "forgejo", "github", "inputs", "vars", "secrets", 
"env"],
+      "mapping": {
+        "properties": {
+          "username": "non-empty-string",
+          "password": "non-empty-string"
+        }
+      }
+    },
+    "container-env": {
+      "description": "Use `jobs.<job_id>.container.env` to set a map of 
variables in the container.",
+      "mapping": {
+        "loose-key-type": "non-empty-string",
+        "loose-value-type": "string-runner-context"
+      }
+    },
     "services": {
-      "description": "Additional containers to host services for a job in a 
workflow. These are useful for creating databases or cache services like redis. 
The runner on the virtual machine will automatically create a network and 
manage the life cycle of the service containers. When you use a service 
container for a job or your step uses container actions, you don't need to set 
port information to access the service. Docker automatically exposes all ports 
between containers on the same network. When both the job and the action run in 
a container, you can directly reference the container by its hostname. The 
hostname is automatically mapped to the service name. When a step does not use 
a container action, you must access the service using localhost and bind the 
ports.",
+      "description": "Additional containers to host services for a job in a 
workflow. These are useful for creating databases or cache services like Redis. 
The runner on the virtual machine will automatically create a network and 
manage the life cycle of the service containers. When you use a service 
container for a job or your step uses container actions, you don't need to set 
port information to access the service. Docker automatically exposes all ports 
between containers on the same network. When both the job and the action run in 
a container, you can directly reference the container by its hostname. The 
hostname is automatically mapped to the service name. When a step does not use 
a container action, you must access the service using localhost and bind the 
ports.",
       "context": ["forge", "forgejo", "github", "inputs", "vars", "needs", 
"strategy", "matrix", "env"],
       "mapping": {
         "loose-key-type": "non-empty-string",
-        "loose-value-type": "services-container"
+        "loose-value-type": "service"
       }
     },
-    "services-container": {
+    "service": {
       "context": ["forge", "forgejo", "github", "inputs", "vars", "needs", 
"strategy", "matrix", "env"],
-      "one-of": ["non-empty-string", "container-mapping"]
+      "one-of": ["non-empty-string", "service-mapping"]
     },
-    "container-registry-credentials": {
-      "description": "If the image's container registry requires 
authentication to pull the image, you can use 
`jobs.<job_id>.container.credentials` to set a map of the username and 
password. The credentials are the same values that you would provide to the 
`docker login` command.",
-      "context": ["forge", "forgejo", "github", "inputs", "vars", "secrets", 
"env"],
+    "service-mapping": {
       "mapping": {
         "properties": {
-          "username": "non-empty-string",
-          "password": "non-empty-string"
+          "image": {
+            "type": "non-empty-string",
+            "description": "Name of the container image that contains the 
service to run. The value can be a short name like `fedora` or a fully 
qualified image name like `registry.fedoraproject.org/fedora`."
+          },
+          "options": {
+            "type": "non-empty-string",
+            "description": "Additional options to pass to the container 
runtime."
+          },
+          "cmd": {
+            "type": "sequence-of-non-empty-string",
+            "description": "List of commands to run instead of the default 
command."
+          },
+          "env": "service-env",
+          "ports": {
+            "type": "sequence-of-non-empty-string",
+            "description": "List of service container ports to expose in the 
format `HOST_PORT:CONTAINER_PORT`"
+          },
+          "volumes": {
+            "type": "sequence-of-non-empty-string",
+            "description": "List of volumes to mount into the service 
container. You can specify named volumes, anonymous volumes, or bind mounts."
+          },
+          "credentials": "service-container-registry-credentials"
         }
       }
     },
-    "container-env": {
-      "description": "Use `jobs.<job_id>.container.env` to set a map of 
variables in the container.",
+    "service-env": {
+      "description": "A map of environment variables that should be injected 
into the service container during startup.",
       "mapping": {
         "loose-key-type": "non-empty-string",
         "loose-value-type": "string-runner-context"
       }
     },
+    "service-container-registry-credentials": {
+      "description": "If the image's container registry requires 
authentication to pull the image, you can specify the username and password to 
use when pulling the image.",
+      "context": ["forge", "forgejo", "github", "inputs", "vars", "secrets", 
"env"],
+      "mapping": {
+        "properties": {
+          "username": "non-empty-string",
+          "password": "non-empty-string"
+        }
+      }
+    },
     "non-empty-string": {
       "string": {
         "require-non-empty": true
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/forgejo-runner-12.3.1/examples/docker-compose/compose-forgejo-and-runner.yml
 
new/forgejo-runner-12.4.0/examples/docker-compose/compose-forgejo-and-runner.yml
--- 
old/forgejo-runner-12.3.1/examples/docker-compose/compose-forgejo-and-runner.yml
    2025-12-24 17:06:29.000000000 +0100
+++ 
new/forgejo-runner-12.4.0/examples/docker-compose/compose-forgejo-and-runner.yml
    2025-12-31 15:18:43.000000000 +0100
@@ -51,7 +51,7 @@
       - 8080:3000
 
   runner-register:
-    image: code.forgejo.org/forgejo/runner:12.0.1
+    image: code.forgejo.org/forgejo/runner:12.3.1
     links:
       - docker-in-docker
       - forgejo
@@ -77,7 +77,7 @@
       '
 
   runner-daemon:
-    image: code.forgejo.org/forgejo/runner:12.0.1
+    image: code.forgejo.org/forgejo/runner:12.3.1
     links:
       - docker-in-docker
       - forgejo
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/forgejo-runner-12.3.1/examples/lxc-systemd/forgejo-runner-service.sh 
new/forgejo-runner-12.4.0/examples/lxc-systemd/forgejo-runner-service.sh
--- old/forgejo-runner-12.3.1/examples/lxc-systemd/forgejo-runner-service.sh    
2025-12-24 17:06:29.000000000 +0100
+++ new/forgejo-runner-12.4.0/examples/lxc-systemd/forgejo-runner-service.sh    
2025-12-31 15:18:43.000000000 +0100
@@ -22,7 +22,7 @@
 : ${INPUTS_LIFETIME:=7d}
 DEFAULT_LXC_HELPERS_VERSION=1.1.3 # renovate: datasource=forgejo-tags 
depName=forgejo/lxc-helpers
 : ${INPUTS_LXC_HELPERS_VERSION:=$DEFAULT_LXC_HELPERS_VERSION}
-DEFAULT_RUNNER_VERSION=12.0.1 # renovate: datasource=forgejo-releases 
depName=forgejo/runner
+DEFAULT_RUNNER_VERSION=12.3.1 # renovate: datasource=forgejo-releases 
depName=forgejo/runner
 : ${INPUTS_RUNNER_VERSION:=$DEFAULT_RUNNER_VERSION}
 
 : ${KILL_AFTER:=21600} # 6h == 21600
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/forgejo-runner-12.3.1/go.mod 
new/forgejo-runner-12.4.0/go.mod
--- old/forgejo-runner-12.3.1/go.mod    2025-12-24 17:06:29.000000000 +0100
+++ new/forgejo-runner-12.4.0/go.mod    2025-12-31 15:18:43.000000000 +0100
@@ -1,11 +1,11 @@
 module code.forgejo.org/forgejo/runner/v12
 
-go 1.24.0
+go 1.25.0
 
-toolchain go1.24.10
+toolchain go1.25.5
 
 require (
-       code.forgejo.org/forgejo/actions-proto v0.5.3
+       code.forgejo.org/forgejo/actions-proto v0.6.0
        connectrpc.com/connect v1.19.1
        dario.cat/mergo v1.0.2
        github.com/Masterminds/semver v1.5.0
@@ -37,9 +37,10 @@
        github.com/timshannon/bolthold v0.0.0-20240314194003-30aac6950928
        go.etcd.io/bbolt v1.4.3
        go.yaml.in/yaml/v3 v3.0.4
+       golang.org/x/sys v0.38.0
        golang.org/x/term v0.37.0
        golang.org/x/time v0.14.0
-       google.golang.org/protobuf v1.36.10
+       google.golang.org/protobuf v1.36.11
        gotest.tools/v3 v3.5.2
 )
 
@@ -103,7 +104,6 @@
        golang.org/x/crypto v0.45.0 // indirect
        golang.org/x/net v0.47.0 // indirect
        golang.org/x/sync v0.17.0 // indirect
-       golang.org/x/sys v0.38.0 // indirect
        google.golang.org/genproto/googleapis/api 
v0.0.0-20231120223509-83a465c0220f // indirect
        gopkg.in/warnings.v0 v0.1.2 // indirect
        gopkg.in/yaml.v3 v3.0.1 // indirect
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/forgejo-runner-12.3.1/go.sum 
new/forgejo-runner-12.4.0/go.sum
--- old/forgejo-runner-12.3.1/go.sum    2025-12-24 17:06:29.000000000 +0100
+++ new/forgejo-runner-12.4.0/go.sum    2025-12-31 15:18:43.000000000 +0100
@@ -1,5 +1,5 @@
-code.forgejo.org/forgejo/actions-proto v0.5.3 
h1:dDProRNB4CDvEl9gfo8jkiVfGdiW7fXAt5TM9Irka28=
-code.forgejo.org/forgejo/actions-proto v0.5.3/go.mod 
h1:33iTdur/jVa/wAQP+BuciRTK9WZcVaxy0BNEnSWWFDM=
+code.forgejo.org/forgejo/actions-proto v0.6.0 
h1:dw1Dogk9A4V/yrLVkhe9dSZPsqNAIkI1kCXPSqG3tZA=
+code.forgejo.org/forgejo/actions-proto v0.6.0/go.mod 
h1:+444hHBs9/qDh5X/AedaTv0Egj3vd/EXP93vg9zFV2E=
 connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14=
 connectrpc.com/connect v1.19.1/go.mod 
h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w=
 cyphar.com/go-pathrs v0.2.1 h1:9nx1vOgwVvX1mNBWDu93+vaceedpbsDqo+XuBGL40b8=
@@ -264,8 +264,8 @@
 google.golang.org/genproto/googleapis/rpc 
v0.0.0-20240903143218-8af14fe29dc1/go.mod 
h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
 google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw=
 google.golang.org/grpc v1.67.0/go.mod 
h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
-google.golang.org/protobuf v1.36.10 
h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
-google.golang.org/protobuf v1.36.10/go.mod 
h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
+google.golang.org/protobuf v1.36.11 
h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
+google.golang.org/protobuf v1.36.11/go.mod 
h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c 
h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/forgejo-runner-12.3.1/internal/app/poll/poller.go 
new/forgejo-runner-12.4.0/internal/app/poll/poller.go
--- old/forgejo-runner-12.3.1/internal/app/poll/poller.go       2025-12-24 
17:06:29.000000000 +0100
+++ new/forgejo-runner-12.4.0/internal/app/poll/poller.go       2025-12-31 
15:18:43.000000000 +0100
@@ -70,12 +70,39 @@
 
 func (p *poller) Poll() {
        limiter := rate.NewLimiter(rate.Every(p.cfg.Runner.FetchInterval), 1)
+
+       capacity := int64(p.cfg.Runner.Capacity)
+       inProgressTasks := atomic.Int64{}
        wg := &sync.WaitGroup{}
-       for i := 0; i < p.cfg.Runner.Capacity; i++ {
-               wg.Add(1)
-               go p.poll(i, wg, limiter)
+
+       log.Infof("[poller] launched")
+       for {
+               if err := limiter.Wait(p.pollingCtx); err != nil {
+                       log.Infof("[poller] shutdown begin, %d tasks currently 
running", inProgressTasks.Load())
+                       break
+               }
+
+               availableCapacity := capacity - inProgressTasks.Load()
+               if availableCapacity > 0 {
+                       log.Tracef("[poller] fetching at most %d tasks", 
availableCapacity)
+                       tasks, ok := p.fetchTasks(p.pollingCtx, 
availableCapacity)
+                       if !ok {
+                               continue
+                       }
+
+                       log.Tracef("[poller] successfully fetched %d tasks", 
len(tasks))
+                       for _, task := range tasks {
+                               inProgressTasks.Add(1)
+                               wg.Go(func() {
+                                       p.runTaskWithRecover(p.jobsCtx, task)
+                                       inProgressTasks.Add(-1)
+                               })
+                       }
+               }
        }
+
        wg.Wait()
+       log.Trace("[poller] shutdown complete, all tasks complete")
 
        // signal the poller is finished
        close(p.done)
@@ -98,22 +125,6 @@
        }
 }
 
-func (p *poller) poll(id int, wg *sync.WaitGroup, limiter *rate.Limiter) {
-       log.Infof("[poller %d] launched", id)
-       defer wg.Done()
-       for {
-               if err := limiter.Wait(p.pollingCtx); err != nil {
-                       log.Infof("[poller %d] shutdown", id)
-                       return
-               }
-               task, ok := p.fetchTask(p.pollingCtx)
-               if !ok {
-                       continue
-               }
-               p.runTaskWithRecover(p.jobsCtx, task)
-       }
-}
-
 func (p *poller) runTaskWithRecover(ctx context.Context, task *runnerv1.Task) {
        defer func() {
                if r := recover(); r != nil {
@@ -127,7 +138,11 @@
        }
 }
 
-func (p *poller) fetchTask(ctx context.Context) (*runnerv1.Task, bool) {
+func (p *poller) fetchTasks(ctx context.Context, availableCapacity int64) 
([]*runnerv1.Task, bool) {
+       if availableCapacity == 0 {
+               return nil, false
+       }
+
        reqCtx, cancel := context.WithTimeout(ctx, p.cfg.Runner.FetchTimeout)
        defer cancel()
 
@@ -135,6 +150,7 @@
        v := p.tasksVersion.Load()
        resp, err := p.client.FetchTask(reqCtx, 
connect.NewRequest(&runnerv1.FetchTaskRequest{
                TasksVersion: v,
+               TaskCapacity: &availableCapacity,
        }))
        if errors.Is(err, context.DeadlineExceeded) {
                log.Trace("deadline exceeded")
@@ -161,8 +177,11 @@
                return nil, false
        }
 
-       // got a task, set `tasksVersion` to zero to focre query db in next 
request.
+       // got a task, set `tasksVersion` to zero to force query db in next 
request.
        p.tasksVersion.CompareAndSwap(resp.Msg.GetTasksVersion(), 0)
 
-       return resp.Msg.GetTask(), true
+       taskSlice := []*runnerv1.Task{resp.Msg.GetTask()}
+       taskSlice = append(taskSlice, resp.Msg.GetAdditionalTasks()...)
+
+       return taskSlice, true
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/forgejo-runner-12.3.1/internal/app/poll/poller_test.go 
new/forgejo-runner-12.4.0/internal/app/poll/poller_test.go
--- old/forgejo-runner-12.3.1/internal/app/poll/poller_test.go  2025-12-24 
17:06:29.000000000 +0100
+++ new/forgejo-runner-12.4.0/internal/app/poll/poller_test.go  2025-12-31 
15:18:43.000000000 +0100
@@ -7,6 +7,7 @@
        "context"
        "fmt"
        "testing"
+       "testing/synctest"
        "time"
 
        "connectrpc.com/connect"
@@ -14,10 +15,14 @@
        "code.forgejo.org/forgejo/actions-proto/ping/v1/pingv1connect"
        runnerv1 "code.forgejo.org/forgejo/actions-proto/runner/v1"
        "code.forgejo.org/forgejo/actions-proto/runner/v1/runnerv1connect"
+       mock_runner "code.forgejo.org/forgejo/runner/v12/internal/app/run/mocks"
+       "code.forgejo.org/forgejo/runner/v12/internal/pkg/client/mocks"
        "code.forgejo.org/forgejo/runner/v12/internal/pkg/config"
 
        log "github.com/sirupsen/logrus"
        "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/mock"
+       "github.com/stretchr/testify/require"
 )
 
 type mockPoller struct {
@@ -32,10 +37,11 @@
        pingv1connect.PingServiceClient
        runnerv1connect.RunnerServiceClient
 
-       sleep  time.Duration
-       cancel bool
-       err    error
-       noTask bool
+       sleep     time.Duration
+       cancel    bool
+       err       error
+       noTask    bool
+       addtTasks bool
 }
 
 func (o mockClient) Address() string {
@@ -69,9 +75,15 @@
                o.noTask = false
        }
 
+       var addt []*runnerv1.Task
+       if o.addtTasks {
+               addt = append(addt, &runnerv1.Task{})
+       }
+
        return connect.NewResponse(&runnerv1.FetchTaskResponse{
-               Task:         task,
-               TasksVersion: int64(1),
+               Task:            task,
+               TasksVersion:    int64(1),
+               AdditionalTasks: addt,
        }), nil
 }
 
@@ -206,16 +218,19 @@
 func TestPoller_Fetch(t *testing.T) {
        setTrace(t)
        for _, testCase := range []struct {
-               name    string
-               noTask  bool
-               sleep   time.Duration
-               err     error
-               cancel  bool
-               success bool
+               name      string
+               noTask    bool
+               sleep     time.Duration
+               err       error
+               cancel    bool
+               success   bool
+               addtTasks bool
+               taskCount int
        }{
                {
-                       name:    "Success",
-                       success: true,
+                       name:      "Success",
+                       success:   true,
+                       taskCount: 1,
                },
                {
                        name:  "Timeout",
@@ -230,6 +245,12 @@
                        noTask: true,
                },
                {
+                       name:      "AdditionalTasks",
+                       success:   true,
+                       addtTasks: true,
+                       taskCount: 2,
+               },
+               {
                        name: "Error",
                        err:  fmt.Errorf("random error"),
                },
@@ -245,17 +266,19 @@
                                        Runner: configRunner,
                                },
                                &mockClient{
-                                       sleep:  testCase.sleep,
-                                       cancel: testCase.cancel,
-                                       noTask: testCase.noTask,
-                                       err:    testCase.err,
+                                       sleep:     testCase.sleep,
+                                       cancel:    testCase.cancel,
+                                       noTask:    testCase.noTask,
+                                       err:       testCase.err,
+                                       addtTasks: testCase.addtTasks,
                                },
                                &mockRunner{},
                        )
-                       task, ok := p.fetchTask(context.Background())
+                       task, ok := p.fetchTasks(context.Background(), 100)
                        if testCase.success {
                                assert.True(t, ok)
                                assert.NotNil(t, task)
+                               assert.Len(t, task, testCase.taskCount)
                        } else {
                                assert.False(t, ok)
                                assert.Nil(t, task)
@@ -263,3 +286,191 @@
                })
        }
 }
+
+func TestPollerPoll(t *testing.T) {
+       setup := func(t *testing.T, pollingCtx context.Context) (*mocks.Client, 
*mock_runner.RunnerInterface, Poller) {
+               mockClient := mocks.NewClient(t)
+               mockRunner := mock_runner.NewRunnerInterface(t)
+               poller := New(pollingCtx, &config.Config{
+                       Runner: config.Runner{
+                               Capacity:      3,
+                               FetchInterval: 1 * time.Second,
+                       },
+               }, mockClient, mockRunner)
+               return mockClient, mockRunner, poller
+       }
+       teardown := func(t *testing.T, mockClient *mocks.Client, mockRunner 
*mock_runner.RunnerInterface) {
+               mockClient.AssertExpectations(t)
+               mockRunner.AssertExpectations(t)
+       }
+       emptyResponse := connect.NewResponse(&runnerv1.FetchTaskResponse{
+               Task:            nil,
+               TasksVersion:    int64(1),
+               AdditionalTasks: nil,
+       })
+       task1 := &runnerv1.Task{}
+       task2 := &runnerv1.Task{}
+       task3 := &runnerv1.Task{}
+       twoTaskResponse := connect.NewResponse(&runnerv1.FetchTaskResponse{
+               Task:            task1,
+               TasksVersion:    int64(1),
+               AdditionalTasks: []*runnerv1.Task{task2},
+       })
+       threeTaskResponse := connect.NewResponse(&runnerv1.FetchTaskResponse{
+               Task:            task1,
+               TasksVersion:    int64(1),
+               AdditionalTasks: []*runnerv1.Task{task2, task3},
+       })
+
+       // invocations of `fetchTasks` are rate limited per configuration
+       t.Run("fetchTasks rate limited", func(t *testing.T) {
+               synctest.Test(t, func(t *testing.T) {
+                       pollingCtx, cancel := context.WithCancel(t.Context())
+                       defer cancel()
+
+                       mockClient, mockRunner, poller := setup(t, pollingCtx)
+                       mockClient.On("FetchTask", mock.Anything, 
mock.Anything).Return(emptyResponse, nil)
+
+                       go poller.Poll()
+
+                       time.Sleep(1 * time.Millisecond) // should immediately 
FetchTask
+                       mockClient.AssertNumberOfCalls(t, "FetchTask", 1)
+
+                       time.Sleep(998 * time.Millisecond) // not again until 
all of FetchInterval (1s) passes
+                       mockClient.AssertNumberOfCalls(t, "FetchTask", 1)
+
+                       time.Sleep(3 * time.Millisecond) // but then it does 
FetchTask again
+                       mockClient.AssertNumberOfCalls(t, "FetchTask", 2)
+
+                       require.NoError(t, poller.Shutdown(t.Context()))
+
+                       teardown(t, mockClient, mockRunner)
+               })
+       })
+
+       t.Run("available capacity is passed to fetchTask", func(t *testing.T) {
+               synctest.Test(t, func(t *testing.T) {
+                       pollingCtx, cancel := context.WithCancel(t.Context())
+                       defer cancel()
+
+                       mockClient, mockRunner, poller := setup(t, pollingCtx)
+                       mockClient.On("FetchTask", mock.Anything, 
mock.Anything).
+                               Once().
+                               Run(func(args mock.Arguments) {
+                                       req := 
args.Get(1).(*connect.Request[runnerv1.FetchTaskRequest])
+                                       assert.EqualValues(t, 0, 
req.Msg.GetTasksVersion(), "GetTasksVersion")
+                                       assert.EqualValues(t, 3, 
req.Msg.GetTaskCapacity(), "GetTaskCapacity")
+                               }).
+                               Return(emptyResponse, nil)
+
+                       go poller.Poll()
+                       time.Sleep(1 * time.Millisecond)
+                       require.NoError(t, poller.Shutdown(t.Context()))
+
+                       teardown(t, mockClient, mockRunner)
+               })
+       })
+
+       t.Run("available capacity is varied as tasks start and finish", func(t 
*testing.T) {
+               synctest.Test(t, func(t *testing.T) {
+                       pollingCtx, cancel := context.WithCancel(t.Context())
+                       defer cancel()
+
+                       mockClient, mockRunner, poller := setup(t, pollingCtx)
+                       // First fetch -- assert 3 capacity requested, return 
two tasks
+                       mockClient.On("FetchTask", mock.Anything, 
mock.Anything).
+                               Once().
+                               Run(func(args mock.Arguments) {
+                                       req := 
args.Get(1).(*connect.Request[runnerv1.FetchTaskRequest])
+                                       assert.EqualValues(t, 0, 
req.Msg.GetTasksVersion(), "GetTasksVersion Call 1")
+                                       assert.EqualValues(t, 3, 
req.Msg.GetTaskCapacity(), "GetTaskCapacity Call 1")
+                               }).
+                               Return(twoTaskResponse, nil)
+                       // Second fetch -- assert 1 capacity, return no tasks
+                       mockClient.On("FetchTask", mock.Anything, 
mock.Anything).
+                               Once().
+                               Run(func(args mock.Arguments) {
+                                       req := 
args.Get(1).(*connect.Request[runnerv1.FetchTaskRequest])
+                                       assert.EqualValues(t, 0, 
req.Msg.GetTasksVersion(), "GetTasksVersion Call 2")
+                                       assert.EqualValues(t, 1, 
req.Msg.GetTaskCapacity(), "GetTaskCapacity Call 2")
+                               }).
+                               Return(emptyResponse, nil)
+                       // Third fetch -- assert 3 capacity, return no tasks
+                       mockClient.On("FetchTask", mock.Anything, 
mock.Anything).
+                               Once().
+                               Run(func(args mock.Arguments) {
+                                       req := 
args.Get(1).(*connect.Request[runnerv1.FetchTaskRequest])
+                                       assert.EqualValues(t, 1, 
req.Msg.GetTasksVersion(), "GetTasksVersion Call 3")
+                                       assert.EqualValues(t, 3, 
req.Msg.GetTaskCapacity(), "GetTaskCapacity Call 3")
+                               }).
+                               Return(emptyResponse, nil)
+                       mockRunner.On("Run", mock.Anything, mock.Anything).
+                               Run(func(args mock.Arguments) {
+                                       // Take some time to execute so that 
the 2nd FetchTask still has these tasks considered in-progress.
+                                       time.Sleep(1500 * time.Millisecond)
+                               }).
+                               Return(nil)
+
+                       go poller.Poll()
+                       time.Sleep(1 * time.Millisecond)
+                       mockClient.AssertNumberOfCalls(t, "FetchTask", 1)
+                       time.Sleep(1 * time.Second)
+                       mockClient.AssertNumberOfCalls(t, "FetchTask", 2)
+                       time.Sleep(1 * time.Second)
+                       mockClient.AssertNumberOfCalls(t, "FetchTask", 3)
+                       require.NoError(t, poller.Shutdown(t.Context()))
+
+                       teardown(t, mockClient, mockRunner)
+               })
+       })
+
+       t.Run("no FetchTask when available capacity is zero", func(t 
*testing.T) {
+               synctest.Test(t, func(t *testing.T) {
+                       pollingCtx, cancel := context.WithCancel(t.Context())
+                       defer cancel()
+
+                       mockClient, mockRunner, poller := setup(t, pollingCtx)
+                       mockClient.On("FetchTask", mock.Anything, 
mock.Anything).
+                               Return(threeTaskResponse, nil)
+                       mockRunner.On("Run", mock.Anything, mock.Anything).
+                               Run(func(args mock.Arguments) {
+                                       time.Sleep(1 * time.Hour)
+                               }).
+                               Return(nil)
+
+                       go poller.Poll()
+                       time.Sleep(1 * time.Millisecond)
+                       mockClient.AssertNumberOfCalls(t, "FetchTask", 1)
+                       time.Sleep(30 * time.Minute) // a long time later, but 
jobs are using up all the capacity...
+                       mockClient.AssertNumberOfCalls(t, "FetchTask", 1)
+                       require.NoError(t, poller.Shutdown(t.Context()))
+
+                       teardown(t, mockClient, mockRunner)
+               })
+       })
+
+       t.Run("poll shutdown waits for task completion", func(t *testing.T) {
+               synctest.Test(t, func(t *testing.T) {
+                       pollingCtx, cancel := context.WithCancel(t.Context())
+                       defer cancel()
+
+                       mockClient, mockRunner, poller := setup(t, pollingCtx)
+                       mockClient.On("FetchTask", mock.Anything, 
mock.Anything).
+                               Return(twoTaskResponse, nil)
+                       mockRunner.On("Run", mock.Anything, mock.Anything).
+                               Run(func(args mock.Arguments) {
+                                       time.Sleep(1 * time.Hour)
+                               }).
+                               Return(nil)
+
+                       go poller.Poll()
+                       time.Sleep(1 * time.Millisecond) // let poll get 
started, fetch tasks, start them
+                       shutdownStart := time.Now()
+                       require.NoError(t, poller.Shutdown(t.Context()))
+                       shutdownEnd := time.Now()
+                       assert.EqualValues(t, 3599999000, 
shutdownEnd.Sub(shutdownStart).Microseconds())
+
+                       teardown(t, mockClient, mockRunner)
+               })
+       })
+}

++++++ forgejo-runner.obsinfo ++++++
--- /var/tmp/diff_new_pack.9rCpS4/_old  2026-01-05 14:56:58.279786406 +0100
+++ /var/tmp/diff_new_pack.9rCpS4/_new  2026-01-05 14:56:58.295787072 +0100
@@ -1,5 +1,5 @@
 name: forgejo-runner
-version: 12.3.1
-mtime: 1766592389
-commit: 4e1da369ef8c5e6ce963b0121393af6f78d0df0b
+version: 12.4.0
+mtime: 1767190723
+commit: a4f4474fed53d9a687e4363116b07b33c1f6f66f
 

++++++ vendor.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/vendor/code.forgejo.org/forgejo/actions-proto/runner/v1/messages.pb.go 
new/vendor/code.forgejo.org/forgejo/actions-proto/runner/v1/messages.pb.go
--- old/vendor/code.forgejo.org/forgejo/actions-proto/runner/v1/messages.pb.go  
2025-12-24 17:06:29.000000000 +0100
+++ new/vendor/code.forgejo.org/forgejo/actions-proto/runner/v1/messages.pb.go  
2025-12-31 15:18:43.000000000 +0100
@@ -384,7 +384,10 @@
        sizeCache     protoimpl.SizeCache
        unknownFields protoimpl.UnknownFields
 
-       TasksVersion int64 
`protobuf:"varint,1,opt,name=tasks_version,json=tasksVersion,proto3" 
json:"tasks_version,omitempty"` // Runner use `tasks_version` to compare with 
Gitea and detemine whether new tasks may exist.
+       // Runner use `tasks_version` to compare with Gitea and detemine 
whether new tasks may exist.
+       TasksVersion int64 
`protobuf:"varint,1,opt,name=tasks_version,json=tasksVersion,proto3" 
json:"tasks_version,omitempty"`
+       // If provided and `>1`, then multiple tasks may be returned from 
FetchTask in `additional_tasks`.
+       TaskCapacity *int64 
`protobuf:"varint,2,opt,name=task_capacity,json=taskCapacity,proto3,oneof" 
json:"task_capacity,omitempty"`
 }
 
 func (x *FetchTaskRequest) Reset() {
@@ -426,13 +429,25 @@
        return 0
 }
 
+func (x *FetchTaskRequest) GetTaskCapacity() int64 {
+       if x != nil && x.TaskCapacity != nil {
+               return *x.TaskCapacity
+       }
+       return 0
+}
+
 type FetchTaskResponse struct {
        state         protoimpl.MessageState
        sizeCache     protoimpl.SizeCache
        unknownFields protoimpl.UnknownFields
 
-       Task         *Task `protobuf:"bytes,1,opt,name=task,proto3" 
json:"task,omitempty"`
-       TasksVersion int64 
`protobuf:"varint,2,opt,name=tasks_version,json=tasksVersion,proto3" 
json:"tasks_version,omitempty"` // Gitea informs the Runner of the latest 
version of tasks through `tasks_version`.
+       Task *Task `protobuf:"bytes,1,opt,name=task,proto3" 
json:"task,omitempty"`
+       // Gitea informs the Runner of the latest version of tasks through 
`tasks_version`.
+       TasksVersion int64 
`protobuf:"varint,2,opt,name=tasks_version,json=tasksVersion,proto3" 
json:"tasks_version,omitempty"`
+       // If `task_capacity` was provided in `FetchTaskRequest`, then 
additional_tasks may indicate multiple assigned tasks
+       // to this runner which are returned in `additional_tasks`. `task` 
would always be populated for the first task, and
+       // the first task is omitted from this collection.
+       AdditionalTasks []*Task 
`protobuf:"bytes,3,rep,name=additional_tasks,json=additionalTasks,proto3" 
json:"additional_tasks,omitempty"`
 }
 
 func (x *FetchTaskResponse) Reset() {
@@ -481,6 +496,13 @@
        return 0
 }
 
+func (x *FetchTaskResponse) GetAdditionalTasks() []*Task {
+       if x != nil {
+               return x.AdditionalTasks
+       }
+       return nil
+}
+
 type UpdateTaskRequest struct {
        state         protoimpl.MessageState
        sizeCache     protoimpl.SizeCache
@@ -1246,171 +1268,178 @@
        0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x06, 
0x72, 0x75, 0x6e, 0x6e,
        0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x72, 
0x75, 0x6e, 0x6e, 0x65,
        0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x52, 
0x06, 0x72, 0x75, 0x6e,
-       0x6e, 0x65, 0x72, 0x22, 0x37, 0x0a, 0x10, 0x46, 0x65, 0x74, 0x63, 0x68, 
0x54, 0x61, 0x73, 0x6b,
+       0x6e, 0x65, 0x72, 0x22, 0x73, 0x0a, 0x10, 0x46, 0x65, 0x74, 0x63, 0x68, 
0x54, 0x61, 0x73, 0x6b,
        0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x74, 
0x61, 0x73, 0x6b, 0x73,
        0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 
0x28, 0x03, 0x52, 0x0c,
-       0x74, 0x61, 0x73, 0x6b, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 
0x22, 0x5d, 0x0a, 0x11,
-       0x46, 0x65, 0x74, 0x63, 0x68, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 
0x70, 0x6f, 0x6e, 0x73,
-       0x65, 0x12, 0x23, 0x0a, 0x04, 0x74, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, 
0x01, 0x28, 0x0b, 0x32,
-       0x0f, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 
0x54, 0x61, 0x73, 0x6b,
-       0x52, 0x04, 0x74, 0x61, 0x73, 0x6b, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x61, 
0x73, 0x6b, 0x73, 0x5f,
-       0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 
0x03, 0x52, 0x0c, 0x74,
-       0x61, 0x73, 0x6b, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 
0xc0, 0x01, 0x0a, 0x11,
-       0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 
0x71, 0x75, 0x65, 0x73,
-       0x74, 0x12, 0x2a, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 
0x20, 0x01, 0x28, 0x0b,
-       0x32, 0x14, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 
0x2e, 0x54, 0x61, 0x73,
-       0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 
0x65, 0x12, 0x43, 0x0a,
-       0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 
0x28, 0x0b, 0x32, 0x29,
-       0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 
0x70, 0x64, 0x61, 0x74,
-       0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 
0x2e, 0x4f, 0x75, 0x74,
-       0x70, 0x75, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x6f, 
0x75, 0x74, 0x70, 0x75,
-       0x74, 0x73, 0x1a, 0x3a, 0x0a, 0x0c, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 
0x73, 0x45, 0x6e, 0x74,
-       0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 
0x01, 0x28, 0x09, 0x52,
-       0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 
0x65, 0x18, 0x02, 0x20,
-       0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 
0x38, 0x01, 0x22, 0x63,
-       0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x73, 0x6b, 
0x52, 0x65, 0x73, 0x70,
-       0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 
0x65, 0x18, 0x01, 0x20,
-       0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 
0x2e, 0x76, 0x31, 0x2e,
-       0x54, 0x61, 0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 
0x74, 0x61, 0x74, 0x65,
-       0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x6e, 0x74, 0x5f, 0x6f, 0x75, 0x74, 
0x70, 0x75, 0x74, 0x73,
-       0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x6e, 0x74, 
0x4f, 0x75, 0x74, 0x70,
-       0x75, 0x74, 0x73, 0x22, 0x81, 0x01, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 
0x74, 0x65, 0x4c, 0x6f,
-       0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 
0x74, 0x61, 0x73, 0x6b,
-       0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x74, 
0x61, 0x73, 0x6b, 0x49,
-       0x64, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 
0x20, 0x01, 0x28, 0x03,
-       0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x25, 0x0a, 0x04, 0x72, 
0x6f, 0x77, 0x73, 0x18,
-       0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 
0x65, 0x72, 0x2e, 0x76,
-       0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x6f, 0x77, 0x52, 0x04, 0x72, 0x6f, 
0x77, 0x73, 0x12, 0x17,
-       0x0a, 0x07, 0x6e, 0x6f, 0x5f, 0x6d, 0x6f, 0x72, 0x65, 0x18, 0x04, 0x20, 
0x01, 0x28, 0x08, 0x52,
-       0x06, 0x6e, 0x6f, 0x4d, 0x6f, 0x72, 0x65, 0x22, 0x30, 0x0a, 0x11, 0x55, 
0x70, 0x64, 0x61, 0x74,
-       0x65, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 
0x12, 0x1b, 0x0a, 0x09,
-       0x61, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 
0x01, 0x28, 0x03, 0x52,
-       0x08, 0x61, 0x63, 0x6b, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0xa7, 0x02, 
0x0a, 0x06, 0x52, 0x75,
-       0x6e, 0x6e, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 
0x20, 0x01, 0x28, 0x03,
-       0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 
0x18, 0x02, 0x20, 0x01,
-       0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 
0x74, 0x6f, 0x6b, 0x65,
-       0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 
0x65, 0x6e, 0x12, 0x12,
-       0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 
0x52, 0x04, 0x6e, 0x61,
-       0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 
0x18, 0x05, 0x20, 0x01,
-       0x28, 0x0e, 0x32, 0x17, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 
0x76, 0x31, 0x2e, 0x52,
-       0x75, 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 
0x06, 0x73, 0x74, 0x61,
-       0x74, 0x75, 0x73, 0x12, 0x25, 0x0a, 0x0c, 0x61, 0x67, 0x65, 0x6e, 0x74, 
0x5f, 0x6c, 0x61, 0x62,
-       0x65, 0x6c, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x42, 0x02, 0x18, 
0x01, 0x52, 0x0b, 0x61,
-       0x67, 0x65, 0x6e, 0x74, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x27, 
0x0a, 0x0d, 0x63, 0x75,
-       0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 
0x07, 0x20, 0x03, 0x28,
-       0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 
0x6d, 0x4c, 0x61, 0x62,
-       0x65, 0x6c, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 
0x6f, 0x6e, 0x18, 0x08,
-       0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 
0x6e, 0x12, 0x16, 0x0a,
-       0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 
0x09, 0x52, 0x06, 0x6c,
-       0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x70, 0x68, 
0x65, 0x6d, 0x65, 0x72,
-       0x61, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x65, 0x70, 
0x68, 0x65, 0x6d, 0x65,
-       0x72, 0x61, 0x6c, 0x22, 0x9a, 0x04, 0x0a, 0x04, 0x54, 0x61, 0x73, 0x6b, 
0x12, 0x0e, 0x0a, 0x02,
-       0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 
0x12, 0x2e, 0x0a, 0x10,
-       0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x61, 0x79, 
0x6c, 0x6f, 0x61, 0x64,
-       0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x0f, 0x77, 0x6f, 
0x72, 0x6b, 0x66, 0x6c,
-       0x6f, 0x77, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x88, 0x01, 0x01, 
0x12, 0x36, 0x0a, 0x07,
-       0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 
0x0b, 0x32, 0x17, 0x2e,
-       0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 
0x62, 0x75, 0x66, 0x2e,
-       0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x48, 0x01, 0x52, 0x07, 0x63, 0x6f, 
0x6e, 0x74, 0x65, 0x78,
-       0x74, 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x72, 
0x65, 0x74, 0x73, 0x18,
-       0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 
0x65, 0x72, 0x2e, 0x76,
-       0x31, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 
0x74, 0x73, 0x45, 0x6e,
-       0x74, 0x72, 0x79, 0x52, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 
0x12, 0x1c, 0x0a, 0x07,
-       0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 
0x09, 0x42, 0x02, 0x18,
-       0x01, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x30, 
0x0a, 0x05, 0x6e, 0x65,
-       0x65, 0x64, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 
0x72, 0x75, 0x6e, 0x6e,
-       0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x2e, 0x4e, 
0x65, 0x65, 0x64, 0x73,
-       0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x6e, 0x65, 0x65, 0x64, 0x73, 
0x12, 0x2d, 0x0a, 0x04,
-       0x76, 0x61, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 
0x2e, 0x72, 0x75, 0x6e,
-       0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x2e, 
0x56, 0x61, 0x72, 0x73,
-       0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x76, 0x61, 0x72, 0x73, 0x1a, 
0x3a, 0x0a, 0x0c, 0x53,
-       0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 
0x10, 0x0a, 0x03, 0x6b,
+       0x74, 0x61, 0x73, 0x6b, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 
0x12, 0x28, 0x0a, 0x0d,
+       0x74, 0x61, 0x73, 0x6b, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 
0x79, 0x18, 0x02, 0x20,
+       0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x0c, 0x74, 0x61, 0x73, 0x6b, 0x43, 
0x61, 0x70, 0x61, 0x63,
+       0x69, 0x74, 0x79, 0x88, 0x01, 0x01, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x74, 
0x61, 0x73, 0x6b, 0x5f,
+       0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x22, 0x99, 0x01, 0x0a, 
0x11, 0x46, 0x65, 0x74,
+       0x63, 0x68, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 
0x73, 0x65, 0x12, 0x23,
+       0x0a, 0x04, 0x74, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 
0x32, 0x0f, 0x2e, 0x72,
+       0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x73, 
0x6b, 0x52, 0x04, 0x74,
+       0x61, 0x73, 0x6b, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x61, 0x73, 0x6b, 0x73, 
0x5f, 0x76, 0x65, 0x72,
+       0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 
0x74, 0x61, 0x73, 0x6b,
+       0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3a, 0x0a, 0x10, 
0x61, 0x64, 0x64, 0x69,
+       0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x73, 
0x18, 0x03, 0x20, 0x03,
+       0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 
0x76, 0x31, 0x2e, 0x54,
+       0x61, 0x73, 0x6b, 0x52, 0x0f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 
0x6e, 0x61, 0x6c, 0x54,
+       0x61, 0x73, 0x6b, 0x73, 0x22, 0xc0, 0x01, 0x0a, 0x11, 0x55, 0x70, 0x64, 
0x61, 0x74, 0x65, 0x54,
+       0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, 
0x0a, 0x05, 0x73, 0x74,
+       0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 
0x72, 0x75, 0x6e, 0x6e,
+       0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x53, 0x74, 
0x61, 0x74, 0x65, 0x52,
+       0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x43, 0x0a, 0x07, 0x6f, 0x75, 
0x74, 0x70, 0x75, 0x74,
+       0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x72, 0x75, 
0x6e, 0x6e, 0x65, 0x72,
+       0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 
0x73, 0x6b, 0x52, 0x65,
+       0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 
0x73, 0x45, 0x6e, 0x74,
+       0x72, 0x79, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x1a, 
0x3a, 0x0a, 0x0c, 0x4f,
+       0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 
0x10, 0x0a, 0x03, 0x6b,
        0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 
0x79, 0x12, 0x14, 0x0a,
        0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 
0x52, 0x05, 0x76, 0x61,
-       0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4d, 0x0a, 0x0a, 0x4e, 
0x65, 0x65, 0x64, 0x73,
-       0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 
0x18, 0x01, 0x20, 0x01,
-       0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 
0x61, 0x6c, 0x75, 0x65,
-       0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x72, 0x75, 0x6e, 
0x6e, 0x65, 0x72, 0x2e,
-       0x76, 0x31, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x4e, 0x65, 0x65, 0x64, 0x52, 
0x05, 0x76, 0x61, 0x6c,
-       0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x37, 0x0a, 0x09, 0x56, 0x61, 
0x72, 0x73, 0x45, 0x6e,
-       0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 
0x20, 0x01, 0x28, 0x09,
-       0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 
0x75, 0x65, 0x18, 0x02,
-       0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 
0x02, 0x38, 0x01, 0x42,
-       0x13, 0x0a, 0x11, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 
0x5f, 0x70, 0x61, 0x79,
-       0x6c, 0x6f, 0x61, 0x64, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x63, 0x6f, 0x6e, 
0x74, 0x65, 0x78, 0x74,
-       0x22, 0xad, 0x01, 0x0a, 0x08, 0x54, 0x61, 0x73, 0x6b, 0x4e, 0x65, 0x65, 
0x64, 0x12, 0x3a, 0x0a,
-       0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 
0x28, 0x0b, 0x32, 0x20,
-       0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, 
0x61, 0x73, 0x6b, 0x4e,
-       0x65, 0x65, 0x64, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x45, 
0x6e, 0x74, 0x72, 0x79,
-       0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x12, 0x29, 0x0a, 
0x06, 0x72, 0x65, 0x73,
-       0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 
0x72, 0x75, 0x6e, 0x6e,
-       0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 
0x52, 0x06, 0x72, 0x65,
-       0x73, 0x75, 0x6c, 0x74, 0x1a, 0x3a, 0x0a, 0x0c, 0x4f, 0x75, 0x74, 0x70, 
0x75, 0x74, 0x73, 0x45,
+       0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x63, 0x0a, 0x12, 0x55, 
0x70, 0x64, 0x61, 0x74,
+       0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 
0x65, 0x12, 0x2a, 0x0a,
+       0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 
0x32, 0x14, 0x2e, 0x72,
+       0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x73, 
0x6b, 0x53, 0x74, 0x61,
+       0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 
0x0c, 0x73, 0x65, 0x6e,
+       0x74, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x02, 0x20, 
0x03, 0x28, 0x09, 0x52,
+       0x0b, 0x73, 0x65, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 
0x22, 0x81, 0x01, 0x0a,
+       0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x52, 0x65, 
0x71, 0x75, 0x65, 0x73,
+       0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x69, 0x64, 
0x18, 0x01, 0x20, 0x01,
+       0x28, 0x03, 0x52, 0x06, 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x12, 0x14, 
0x0a, 0x05, 0x69, 0x6e,
+       0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x69, 
0x6e, 0x64, 0x65, 0x78,
+       0x12, 0x25, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x03, 0x20, 0x03, 
0x28, 0x0b, 0x32, 0x11,
+       0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 
0x6f, 0x67, 0x52, 0x6f,
+       0x77, 0x52, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x6e, 
0x6f, 0x5f, 0x6d, 0x6f,
+       0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6e, 0x6f, 
0x4d, 0x6f, 0x72, 0x65,
+       0x22, 0x30, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x6f, 
0x67, 0x52, 0x65, 0x73,
+       0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x63, 0x6b, 
0x5f, 0x69, 0x6e, 0x64,
+       0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x61, 0x63, 
0x6b, 0x49, 0x6e, 0x64,
+       0x65, 0x78, 0x22, 0xa7, 0x02, 0x0a, 0x06, 0x52, 0x75, 0x6e, 0x6e, 0x65, 
0x72, 0x12, 0x0e, 0x0a,
+       0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 
0x64, 0x12, 0x12, 0x0a,
+       0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 
0x04, 0x75, 0x75, 0x69,
+       0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 
0x20, 0x01, 0x28, 0x09,
+       0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 
0x61, 0x6d, 0x65, 0x18,
+       0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 
0x2f, 0x0a, 0x06, 0x73,
+       0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 
0x17, 0x2e, 0x72, 0x75,
+       0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x75, 0x6e, 0x6e, 
0x65, 0x72, 0x53, 0x74,
+       0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 
0x12, 0x25, 0x0a, 0x0c,
+       0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 
0x18, 0x06, 0x20, 0x03,
+       0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x61, 0x67, 0x65, 0x6e, 
0x74, 0x4c, 0x61, 0x62,
+       0x65, 0x6c, 0x73, 0x12, 0x27, 0x0a, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 
0x6d, 0x5f, 0x6c, 0x61,
+       0x62, 0x65, 0x6c, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x42, 0x02, 
0x18, 0x01, 0x52, 0x0c,
+       0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 
0x12, 0x18, 0x0a, 0x07,
+       0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 
0x09, 0x52, 0x07, 0x76,
+       0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x61, 
0x62, 0x65, 0x6c, 0x73,
+       0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 
0x6c, 0x73, 0x12, 0x1c,
+       0x0a, 0x09, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x18, 
0x0a, 0x20, 0x01, 0x28,
+       0x08, 0x52, 0x09, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 
0x22, 0x9a, 0x04, 0x0a,
+       0x04, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 
0x01, 0x20, 0x01, 0x28,
+       0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2e, 0x0a, 0x10, 0x77, 0x6f, 0x72, 
0x6b, 0x66, 0x6c, 0x6f,
+       0x77, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 
0x01, 0x28, 0x0c, 0x48,
+       0x00, 0x52, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x50, 
0x61, 0x79, 0x6c, 0x6f,
+       0x61, 0x64, 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 
0x74, 0x65, 0x78, 0x74,
+       0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 
0x67, 0x6c, 0x65, 0x2e,
+       0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 
0x75, 0x63, 0x74, 0x48,
+       0x01, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x88, 0x01, 
0x01, 0x12, 0x36, 0x0a,
+       0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 
0x28, 0x0b, 0x32, 0x1c,
+       0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, 
0x61, 0x73, 0x6b, 0x2e,
+       0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 
0x52, 0x07, 0x73, 0x65,
+       0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x1c, 0x0a, 0x07, 0x6d, 0x61, 0x63, 
0x68, 0x69, 0x6e, 0x65,
+       0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 
0x6d, 0x61, 0x63, 0x68,
+       0x69, 0x6e, 0x65, 0x12, 0x30, 0x0a, 0x05, 0x6e, 0x65, 0x65, 0x64, 0x73, 
0x18, 0x06, 0x20, 0x03,
+       0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 
0x76, 0x31, 0x2e, 0x54,
+       0x61, 0x73, 0x6b, 0x2e, 0x4e, 0x65, 0x65, 0x64, 0x73, 0x45, 0x6e, 0x74, 
0x72, 0x79, 0x52, 0x05,
+       0x6e, 0x65, 0x65, 0x64, 0x73, 0x12, 0x2d, 0x0a, 0x04, 0x76, 0x61, 0x72, 
0x73, 0x18, 0x07, 0x20,
+       0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 
0x2e, 0x76, 0x31, 0x2e,
+       0x54, 0x61, 0x73, 0x6b, 0x2e, 0x56, 0x61, 0x72, 0x73, 0x45, 0x6e, 0x74, 
0x72, 0x79, 0x52, 0x04,
+       0x76, 0x61, 0x72, 0x73, 0x1a, 0x3a, 0x0a, 0x0c, 0x53, 0x65, 0x63, 0x72, 
0x65, 0x74, 0x73, 0x45,
        0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 
0x01, 0x20, 0x01, 0x28,
        0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 
0x6c, 0x75, 0x65, 0x18,
        0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 
0x3a, 0x02, 0x38, 0x01,
-       0x22, 0xe8, 0x01, 0x0a, 0x09, 0x54, 0x61, 0x73, 0x6b, 0x53, 0x74, 0x61, 
0x74, 0x65, 0x12, 0x0e,
-       0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 
0x69, 0x64, 0x12, 0x29,
-       0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 
0x28, 0x0e, 0x32, 0x11,
-       0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 
0x65, 0x73, 0x75, 0x6c,
-       0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x39, 0x0a, 
0x0a, 0x73, 0x74, 0x61,
-       0x72, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 
0x0b, 0x32, 0x1a, 0x2e,
-       0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 
0x62, 0x75, 0x66, 0x2e,
-       0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 
0x74, 0x61, 0x72, 0x74,
-       0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x6f, 0x70, 
0x70, 0x65, 0x64, 0x5f,
-       0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 
0x6f, 0x6f, 0x67, 0x6c,
-       0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 
0x69, 0x6d, 0x65, 0x73,
-       0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, 
0x64, 0x41, 0x74, 0x12,
-       0x2a, 0x0a, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 
0x28, 0x0b, 0x32, 0x14,
-       0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 
0x74, 0x65, 0x70, 0x53,
-       0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x22, 
0xf8, 0x01, 0x0a, 0x09,
-       0x53, 0x74, 0x65, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, 
0x02, 0x69, 0x64, 0x18,
-       0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x29, 0x0a, 
0x06, 0x72, 0x65, 0x73,
-       0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 
0x72, 0x75, 0x6e, 0x6e,
-       0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 
0x52, 0x06, 0x72, 0x65,
-       0x73, 0x75, 0x6c, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 
0x74, 0x65, 0x64, 0x5f,
-       0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 
0x6f, 0x6f, 0x67, 0x6c,
-       0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 
0x69, 0x6d, 0x65, 0x73,
-       0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 
0x64, 0x41, 0x74, 0x12,
-       0x39, 0x0a, 0x0a, 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x61, 
0x74, 0x18, 0x04, 0x20,
-       0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 
0x2e, 0x70, 0x72, 0x6f,
-       0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 
0x61, 0x6d, 0x70, 0x52,
-       0x09, 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1b, 
0x0a, 0x09, 0x6c, 0x6f,
-       0x67, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 
0x03, 0x52, 0x08, 0x6c,
-       0x6f, 0x67, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 
0x6f, 0x67, 0x5f, 0x6c,
-       0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 
0x09, 0x6c, 0x6f, 0x67,
-       0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x52, 0x0a, 0x06, 0x4c, 0x6f, 
0x67, 0x52, 0x6f, 0x77,
-       0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 
0x28, 0x0b, 0x32, 0x1a,
-       0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 
0x6f, 0x62, 0x75, 0x66,
-       0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 
0x74, 0x69, 0x6d, 0x65,
-       0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 
0x02, 0x20, 0x01, 0x28,
-       0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2a, 0x7a, 
0x0a, 0x0c, 0x52, 0x75,
-       0x6e, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1d, 
0x0a, 0x19, 0x52, 0x55,
-       0x4e, 0x4e, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 
0x55, 0x4e, 0x53, 0x50,
-       0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 
0x12, 0x52, 0x55, 0x4e,
-       0x4e, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x49, 
0x44, 0x4c, 0x45, 0x10,
-       0x01, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x55, 0x4e, 0x4e, 0x45, 0x52, 0x5f, 
0x53, 0x54, 0x41, 0x54,
-       0x55, 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x02, 0x12, 
0x19, 0x0a, 0x15, 0x52,
-       0x55, 0x4e, 0x4e, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 
0x5f, 0x4f, 0x46, 0x46,
-       0x4c, 0x49, 0x4e, 0x45, 0x10, 0x03, 0x2a, 0x72, 0x0a, 0x06, 0x52, 0x65, 
0x73, 0x75, 0x6c, 0x74,
-       0x12, 0x16, 0x0a, 0x12, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x55, 
0x4e, 0x53, 0x50, 0x45,
-       0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 
0x52, 0x45, 0x53, 0x55,
-       0x4c, 0x54, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x01, 
0x12, 0x12, 0x0a, 0x0e,
-       0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 
0x52, 0x45, 0x10, 0x02,
-       0x12, 0x14, 0x0a, 0x10, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x43, 
0x41, 0x4e, 0x43, 0x45,
-       0x4c, 0x4c, 0x45, 0x44, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x45, 
0x53, 0x55, 0x4c, 0x54,
-       0x5f, 0x53, 0x4b, 0x49, 0x50, 0x50, 0x45, 0x44, 0x10, 0x04, 0x42, 0x9e, 
0x01, 0x0a, 0x0d, 0x63,
-       0x6f, 0x6d, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 
0x42, 0x0d, 0x4d, 0x65,
-       0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 
0x01, 0x5a, 0x39, 0x63,
-       0x6f, 0x64, 0x65, 0x2e, 0x66, 0x6f, 0x72, 0x67, 0x65, 0x6a, 0x6f, 0x2e, 
0x6f, 0x72, 0x67, 0x2f,
-       0x66, 0x6f, 0x72, 0x67, 0x65, 0x6a, 0x6f, 0x2f, 0x61, 0x63, 0x74, 0x69, 
0x6f, 0x6e, 0x73, 0x2d,
-       0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 
0x2f, 0x76, 0x31, 0x3b,
-       0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x52, 
0x58, 0x58, 0xaa, 0x02,
-       0x09, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x31, 0xca, 0x02, 
0x09, 0x52, 0x75, 0x6e,
-       0x6e, 0x65, 0x72, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x15, 0x52, 0x75, 0x6e, 
0x6e, 0x65, 0x72, 0x5c,
-       0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 
0x74, 0x61, 0xea, 0x02,
-       0x0a, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x3a, 0x3a, 0x56, 0x31, 0x62, 
0x06, 0x70, 0x72, 0x6f,
-       0x74, 0x6f, 0x33,
+       0x1a, 0x4d, 0x0a, 0x0a, 0x4e, 0x65, 0x65, 0x64, 0x73, 0x45, 0x6e, 0x74, 
0x72, 0x79, 0x12, 0x10,
+       0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 
0x03, 0x6b, 0x65, 0x79,
+       0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 
0x01, 0x28, 0x0b, 0x32,
+       0x13, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 
0x54, 0x61, 0x73, 0x6b,
+       0x4e, 0x65, 0x65, 0x64, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 
0x02, 0x38, 0x01, 0x1a,
+       0x37, 0x0a, 0x09, 0x56, 0x61, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 
0x12, 0x10, 0x0a, 0x03,
+       0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 
0x65, 0x79, 0x12, 0x14,
+       0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 
0x09, 0x52, 0x05, 0x76,
+       0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x13, 0x0a, 0x11, 
0x5f, 0x77, 0x6f, 0x72,
+       0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 
0x64, 0x42, 0x0a, 0x0a,
+       0x08, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0xad, 0x01, 
0x0a, 0x08, 0x54, 0x61,
+       0x73, 0x6b, 0x4e, 0x65, 0x65, 0x64, 0x12, 0x3a, 0x0a, 0x07, 0x6f, 0x75, 
0x74, 0x70, 0x75, 0x74,
+       0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x75, 
0x6e, 0x6e, 0x65, 0x72,
+       0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x4e, 0x65, 0x65, 0x64, 
0x2e, 0x4f, 0x75, 0x74,
+       0x70, 0x75, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x6f, 
0x75, 0x74, 0x70, 0x75,
+       0x74, 0x73, 0x12, 0x29, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 
0x18, 0x02, 0x20, 0x01,
+       0x28, 0x0e, 0x32, 0x11, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 
0x76, 0x31, 0x2e, 0x52,
+       0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 
0x74, 0x1a, 0x3a, 0x0a,
+       0x0c, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 
0x79, 0x12, 0x10, 0x0a,
+       0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 
0x6b, 0x65, 0x79, 0x12,
+       0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 
0x28, 0x09, 0x52, 0x05,
+       0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xe8, 0x01, 
0x0a, 0x09, 0x54, 0x61,
+       0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 
0x64, 0x18, 0x01, 0x20,
+       0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x29, 0x0a, 0x06, 0x72, 
0x65, 0x73, 0x75, 0x6c,
+       0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x72, 0x75, 
0x6e, 0x6e, 0x65, 0x72,
+       0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 
0x72, 0x65, 0x73, 0x75,
+       0x6c, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 
0x64, 0x5f, 0x61, 0x74,
+       0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 
0x67, 0x6c, 0x65, 0x2e,
+       0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 
0x65, 0x73, 0x74, 0x61,
+       0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x41, 
0x74, 0x12, 0x39, 0x0a,
+       0x0a, 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 
0x04, 0x20, 0x01, 0x28,
+       0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 
0x72, 0x6f, 0x74, 0x6f,
+       0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 
0x70, 0x52, 0x09, 0x73,
+       0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x41, 0x74, 0x12, 0x2a, 0x0a, 0x05, 
0x73, 0x74, 0x65, 0x70,
+       0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x72, 0x75, 
0x6e, 0x6e, 0x65, 0x72,
+       0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x65, 0x70, 0x53, 0x74, 0x61, 0x74, 
0x65, 0x52, 0x05, 0x73,
+       0x74, 0x65, 0x70, 0x73, 0x22, 0xf8, 0x01, 0x0a, 0x09, 0x53, 0x74, 0x65, 
0x70, 0x53, 0x74, 0x61,
+       0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 
0x28, 0x03, 0x52, 0x02,
+       0x69, 0x64, 0x12, 0x29, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 
0x18, 0x02, 0x20, 0x01,
+       0x28, 0x0e, 0x32, 0x11, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 
0x76, 0x31, 0x2e, 0x52,
+       0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 
0x74, 0x12, 0x39, 0x0a,
+       0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 
0x03, 0x20, 0x01, 0x28,
+       0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 
0x72, 0x6f, 0x74, 0x6f,
+       0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 
0x70, 0x52, 0x09, 0x73,
+       0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 
0x73, 0x74, 0x6f, 0x70,
+       0x70, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 
0x32, 0x1a, 0x2e, 0x67,
+       0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 
0x75, 0x66, 0x2e, 0x54,
+       0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 
0x6f, 0x70, 0x70, 0x65,
+       0x64, 0x41, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 
0x6e, 0x64, 0x65, 0x78,
+       0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6c, 0x6f, 0x67, 0x49, 
0x6e, 0x64, 0x65, 0x78,
+       0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 
0x74, 0x68, 0x18, 0x06,
+       0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6c, 0x6f, 0x67, 0x4c, 0x65, 0x6e, 
0x67, 0x74, 0x68, 0x22,
+       0x52, 0x0a, 0x06, 0x4c, 0x6f, 0x67, 0x52, 0x6f, 0x77, 0x12, 0x2e, 0x0a, 
0x04, 0x74, 0x69, 0x6d,
+       0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 
0x6f, 0x67, 0x6c, 0x65,
+       0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 
0x6d, 0x65, 0x73, 0x74,
+       0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 
0x07, 0x63, 0x6f, 0x6e,
+       0x74, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 
0x63, 0x6f, 0x6e, 0x74,
+       0x65, 0x6e, 0x74, 0x2a, 0x7a, 0x0a, 0x0c, 0x52, 0x75, 0x6e, 0x6e, 0x65, 
0x72, 0x53, 0x74, 0x61,
+       0x74, 0x75, 0x73, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x55, 0x4e, 0x4e, 0x45, 
0x52, 0x5f, 0x53, 0x54,
+       0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 
0x46, 0x49, 0x45, 0x44,
+       0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x55, 0x4e, 0x4e, 0x45, 0x52, 
0x5f, 0x53, 0x54, 0x41,
+       0x54, 0x55, 0x53, 0x5f, 0x49, 0x44, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x18, 
0x0a, 0x14, 0x52, 0x55,
+       0x4e, 0x4e, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 
0x41, 0x43, 0x54, 0x49,
+       0x56, 0x45, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x55, 0x4e, 0x4e, 
0x45, 0x52, 0x5f, 0x53,
+       0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x4f, 0x46, 0x46, 0x4c, 0x49, 0x4e, 
0x45, 0x10, 0x03, 0x2a,
+       0x72, 0x0a, 0x06, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x16, 0x0a, 
0x12, 0x52, 0x45, 0x53,
+       0x55, 0x4c, 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 
0x49, 0x45, 0x44, 0x10,
+       0x00, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 
0x53, 0x55, 0x43, 0x43,
+       0x45, 0x53, 0x53, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x45, 0x53, 
0x55, 0x4c, 0x54, 0x5f,
+       0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x02, 0x12, 0x14, 0x0a, 
0x10, 0x52, 0x45, 0x53,
+       0x55, 0x4c, 0x54, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x4c, 0x45, 
0x44, 0x10, 0x03, 0x12,
+       0x12, 0x0a, 0x0e, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x53, 0x4b, 
0x49, 0x50, 0x50, 0x45,
+       0x44, 0x10, 0x04, 0x42, 0x9e, 0x01, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x2e, 
0x72, 0x75, 0x6e, 0x6e,
+       0x65, 0x72, 0x2e, 0x76, 0x31, 0x42, 0x0d, 0x4d, 0x65, 0x73, 0x73, 0x61, 
0x67, 0x65, 0x73, 0x50,
+       0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x39, 0x63, 0x6f, 0x64, 0x65, 
0x2e, 0x66, 0x6f, 0x72,
+       0x67, 0x65, 0x6a, 0x6f, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x66, 0x6f, 0x72, 
0x67, 0x65, 0x6a, 0x6f,
+       0x2f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2d, 0x70, 0x72, 0x6f, 
0x74, 0x6f, 0x2f, 0x72,
+       0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x3b, 0x72, 0x75, 0x6e, 
0x6e, 0x65, 0x72, 0x76,
+       0x31, 0xa2, 0x02, 0x03, 0x52, 0x58, 0x58, 0xaa, 0x02, 0x09, 0x52, 0x75, 
0x6e, 0x6e, 0x65, 0x72,
+       0x2e, 0x56, 0x31, 0xca, 0x02, 0x09, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 
0x5c, 0x56, 0x31, 0xe2,
+       0x02, 0x15, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x5c, 0x56, 0x31, 0x5c, 
0x47, 0x50, 0x42, 0x4d,
+       0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0a, 0x52, 0x75, 
0x6e, 0x6e, 0x65, 0x72,
+       0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -1458,31 +1487,32 @@
        12, // 0: runner.v1.RegisterResponse.runner:type_name -> 
runner.v1.Runner
        12, // 1: runner.v1.DeclareResponse.runner:type_name -> runner.v1.Runner
        13, // 2: runner.v1.FetchTaskResponse.task:type_name -> runner.v1.Task
-       15, // 3: runner.v1.UpdateTaskRequest.state:type_name -> 
runner.v1.TaskState
-       18, // 4: runner.v1.UpdateTaskRequest.outputs:type_name -> 
runner.v1.UpdateTaskRequest.OutputsEntry
-       15, // 5: runner.v1.UpdateTaskResponse.state:type_name -> 
runner.v1.TaskState
-       17, // 6: runner.v1.UpdateLogRequest.rows:type_name -> runner.v1.LogRow
-       0,  // 7: runner.v1.Runner.status:type_name -> runner.v1.RunnerStatus
-       23, // 8: runner.v1.Task.context:type_name -> google.protobuf.Struct
-       19, // 9: runner.v1.Task.secrets:type_name -> 
runner.v1.Task.SecretsEntry
-       20, // 10: runner.v1.Task.needs:type_name -> runner.v1.Task.NeedsEntry
-       21, // 11: runner.v1.Task.vars:type_name -> runner.v1.Task.VarsEntry
-       22, // 12: runner.v1.TaskNeed.outputs:type_name -> 
runner.v1.TaskNeed.OutputsEntry
-       1,  // 13: runner.v1.TaskNeed.result:type_name -> runner.v1.Result
-       1,  // 14: runner.v1.TaskState.result:type_name -> runner.v1.Result
-       24, // 15: runner.v1.TaskState.started_at:type_name -> 
google.protobuf.Timestamp
-       24, // 16: runner.v1.TaskState.stopped_at:type_name -> 
google.protobuf.Timestamp
-       16, // 17: runner.v1.TaskState.steps:type_name -> runner.v1.StepState
-       1,  // 18: runner.v1.StepState.result:type_name -> runner.v1.Result
-       24, // 19: runner.v1.StepState.started_at:type_name -> 
google.protobuf.Timestamp
-       24, // 20: runner.v1.StepState.stopped_at:type_name -> 
google.protobuf.Timestamp
-       24, // 21: runner.v1.LogRow.time:type_name -> google.protobuf.Timestamp
-       14, // 22: runner.v1.Task.NeedsEntry.value:type_name -> 
runner.v1.TaskNeed
-       23, // [23:23] is the sub-list for method output_type
-       23, // [23:23] is the sub-list for method input_type
-       23, // [23:23] is the sub-list for extension type_name
-       23, // [23:23] is the sub-list for extension extendee
-       0,  // [0:23] is the sub-list for field type_name
+       13, // 3: runner.v1.FetchTaskResponse.additional_tasks:type_name -> 
runner.v1.Task
+       15, // 4: runner.v1.UpdateTaskRequest.state:type_name -> 
runner.v1.TaskState
+       18, // 5: runner.v1.UpdateTaskRequest.outputs:type_name -> 
runner.v1.UpdateTaskRequest.OutputsEntry
+       15, // 6: runner.v1.UpdateTaskResponse.state:type_name -> 
runner.v1.TaskState
+       17, // 7: runner.v1.UpdateLogRequest.rows:type_name -> runner.v1.LogRow
+       0,  // 8: runner.v1.Runner.status:type_name -> runner.v1.RunnerStatus
+       23, // 9: runner.v1.Task.context:type_name -> google.protobuf.Struct
+       19, // 10: runner.v1.Task.secrets:type_name -> 
runner.v1.Task.SecretsEntry
+       20, // 11: runner.v1.Task.needs:type_name -> runner.v1.Task.NeedsEntry
+       21, // 12: runner.v1.Task.vars:type_name -> runner.v1.Task.VarsEntry
+       22, // 13: runner.v1.TaskNeed.outputs:type_name -> 
runner.v1.TaskNeed.OutputsEntry
+       1,  // 14: runner.v1.TaskNeed.result:type_name -> runner.v1.Result
+       1,  // 15: runner.v1.TaskState.result:type_name -> runner.v1.Result
+       24, // 16: runner.v1.TaskState.started_at:type_name -> 
google.protobuf.Timestamp
+       24, // 17: runner.v1.TaskState.stopped_at:type_name -> 
google.protobuf.Timestamp
+       16, // 18: runner.v1.TaskState.steps:type_name -> runner.v1.StepState
+       1,  // 19: runner.v1.StepState.result:type_name -> runner.v1.Result
+       24, // 20: runner.v1.StepState.started_at:type_name -> 
google.protobuf.Timestamp
+       24, // 21: runner.v1.StepState.stopped_at:type_name -> 
google.protobuf.Timestamp
+       24, // 22: runner.v1.LogRow.time:type_name -> google.protobuf.Timestamp
+       14, // 23: runner.v1.Task.NeedsEntry.value:type_name -> 
runner.v1.TaskNeed
+       24, // [24:24] is the sub-list for method output_type
+       24, // [24:24] is the sub-list for method input_type
+       24, // [24:24] is the sub-list for extension type_name
+       24, // [24:24] is the sub-list for extension extendee
+       0,  // [0:24] is the sub-list for field type_name
 }
 
 func init() { file_runner_v1_messages_proto_init() }
@@ -1684,6 +1714,7 @@
                        }
                }
        }
+       file_runner_v1_messages_proto_msgTypes[4].OneofWrappers = 
[]interface{}{}
        file_runner_v1_messages_proto_msgTypes[11].OneofWrappers = 
[]interface{}{}
        type x struct{}
        out := protoimpl.TypeBuilder{
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go 
new/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go
--- old/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go  
2025-12-24 17:06:29.000000000 +0100
+++ new/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go  
2025-12-31 15:18:43.000000000 +0100
@@ -32,7 +32,7 @@
 func Unmarshal(tag string, goType reflect.Type, evs 
protoreflect.EnumValueDescriptors) protoreflect.FieldDescriptor {
        f := new(filedesc.Field)
        f.L0.ParentFile = filedesc.SurrogateProto2
-       f.L1.EditionFeatures = f.L0.ParentFile.L1.EditionFeatures
+       packed := false
        for len(tag) > 0 {
                i := strings.IndexByte(tag, ',')
                if i < 0 {
@@ -108,7 +108,7 @@
                                f.L1.StringName.InitJSON(jsonName)
                        }
                case s == "packed":
-                       f.L1.EditionFeatures.IsPacked = true
+                       packed = true
                case strings.HasPrefix(s, "def="):
                        // The default tag is special in that everything 
afterwards is the
                        // default regardless of the presence of commas.
@@ -121,6 +121,13 @@
                tag = strings.TrimPrefix(tag[i:], ",")
        }
 
+       // Update EditionFeatures after the loop and after we know whether this 
is
+       // a proto2 or proto3 field.
+       f.L1.EditionFeatures = f.L0.ParentFile.L1.EditionFeatures
+       if packed {
+               f.L1.EditionFeatures.IsPacked = true
+       }
+
        // The generator uses the group message name instead of the field name.
        // We obtain the real field name by lowercasing the group name.
        if f.L1.Kind == protoreflect.GroupKind {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go 
new/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go
--- old/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go      
2025-12-24 17:06:29.000000000 +0100
+++ new/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go      
2025-12-31 15:18:43.000000000 +0100
@@ -424,27 +424,34 @@
        return Token{}, d.newSyntaxError("invalid field name: %s", errId(d.in))
 }
 
-// parseTypeName parses Any type URL or extension field name. The name is
-// enclosed in [ and ] characters. The C++ parser does not handle many legal 
URL
-// strings. This implementation is more liberal and allows for the pattern
-// ^[-_a-zA-Z0-9]+([./][-_a-zA-Z0-9]+)*`). Whitespaces and comments are allowed
-// in between [ ], '.', '/' and the sub names.
+// parseTypeName parses an Any type URL or an extension field name. The name is
+// enclosed in [ and ] characters. We allow almost arbitrary type URL prefixes,
+// closely following the text-format spec [1,2]. We implement "ExtensionName |
+// AnyName" as follows (with some exceptions for backwards compatibility):
+//
+// char      = [-_a-zA-Z0-9]
+// url_char  = char | [.~!$&'()*+,;=] | "%", hex, hex
+//
+// Ident         = char, { char }
+// TypeName      = Ident, { ".", Ident } ;
+// UrlPrefix     = url_char, { url_char | "/" } ;
+// ExtensionName = "[", TypeName, "]" ;
+// AnyName       = "[", UrlPrefix, "/", TypeName, "]" ;
+//
+// Additionally, we allow arbitrary whitespace and comments between [ and ].
+//
+// [1] https://protobuf.dev/reference/protobuf/textformat-spec/#characters
+// [2] https://protobuf.dev/reference/protobuf/textformat-spec/#field-names
 func (d *Decoder) parseTypeName() (Token, error) {
-       startPos := len(d.orig) - len(d.in)
        // Use alias s to advance first in order to use d.in for error handling.
-       // Caller already checks for [ as first character.
+       // Caller already checks for [ as first character (d.in[0] == '[').
        s := consume(d.in[1:], 0)
        if len(s) == 0 {
                return Token{}, ErrUnexpectedEOF
        }
 
+       // Collect everything between [ and ] in name.
        var name []byte
-       for len(s) > 0 && isTypeNameChar(s[0]) {
-               name = append(name, s[0])
-               s = s[1:]
-       }
-       s = consume(s, 0)
-
        var closed bool
        for len(s) > 0 && !closed {
                switch {
@@ -452,23 +459,20 @@
                        s = s[1:]
                        closed = true
 
-               case s[0] == '/', s[0] == '.':
-                       if len(name) > 0 && (name[len(name)-1] == '/' || 
name[len(name)-1] == '.') {
-                               return Token{}, d.newSyntaxError("invalid type 
URL/extension field name: %s",
-                                       d.orig[startPos:len(d.orig)-len(s)+1])
-                       }
+               case s[0] == '/' || isTypeNameChar(s[0]) || 
isUrlExtraChar(s[0]):
                        name = append(name, s[0])
-                       s = s[1:]
-                       s = consume(s, 0)
-                       for len(s) > 0 && isTypeNameChar(s[0]) {
-                               name = append(name, s[0])
-                               s = s[1:]
+                       s = consume(s[1:], 0)
+
+               // URL percent-encoded chars
+               case s[0] == '%':
+                       if len(s) < 3 || !isHexChar(s[1]) || !isHexChar(s[2]) {
+                               return Token{}, d.parseTypeNameError(s, 3)
                        }
-                       s = consume(s, 0)
+                       name = append(name, s[0], s[1], s[2])
+                       s = consume(s[3:], 0)
 
                default:
-                       return Token{}, d.newSyntaxError(
-                               "invalid type URL/extension field name: %s", 
d.orig[startPos:len(d.orig)-len(s)+1])
+                       return Token{}, d.parseTypeNameError(s, 1)
                }
        }
 
@@ -476,15 +480,38 @@
                return Token{}, ErrUnexpectedEOF
        }
 
-       // First character cannot be '.'. Last character cannot be '.' or '/'.
-       size := len(name)
-       if size == 0 || name[0] == '.' || name[size-1] == '.' || name[size-1] 
== '/' {
-               return Token{}, d.newSyntaxError("invalid type URL/extension 
field name: %s",
-                       d.orig[startPos:len(d.orig)-len(s)])
+       // Split collected name on last '/' into urlPrefix and typeName (if '/' 
is
+       // present).
+       typeName := name
+       if i := bytes.LastIndexByte(name, '/'); i != -1 {
+               urlPrefix := name[:i]
+               typeName = name[i+1:]
+
+               // urlPrefix may be empty (for backwards compatibility).
+               // If non-empty, it must not start with '/'.
+               if len(urlPrefix) > 0 && urlPrefix[0] == '/' {
+                       return Token{}, d.parseTypeNameError(s, 0)
+               }
+       }
+
+       // typeName must not be empty (note: "" splits to [""]) and all 
identifier
+       // parts must not be empty.
+       for _, ident := range bytes.Split(typeName, []byte{'.'}) {
+               if len(ident) == 0 {
+                       return Token{}, d.parseTypeNameError(s, 0)
+               }
        }
 
+       // typeName must not contain any percent-encoded or special URL chars.
+       for _, b := range typeName {
+               if b == '%' || (b != '.' && isUrlExtraChar(b)) {
+                       return Token{}, d.parseTypeNameError(s, 0)
+               }
+       }
+
+       startPos := len(d.orig) - len(d.in)
+       endPos := len(d.orig) - len(s)
        d.in = s
-       endPos := len(d.orig) - len(d.in)
        d.consume(0)
 
        return Token{
@@ -496,16 +523,32 @@
        }, nil
 }
 
+func (d *Decoder) parseTypeNameError(s []byte, numUnconsumedChars int) error {
+       return d.newSyntaxError(
+               "invalid type URL/extension field name: %s",
+               d.in[:len(d.in)-len(s)+min(numUnconsumedChars, len(s))],
+       )
+}
+
+func isHexChar(b byte) bool {
+       return ('0' <= b && b <= '9') ||
+               ('a' <= b && b <= 'f') ||
+               ('A' <= b && b <= 'F')
+}
+
 func isTypeNameChar(b byte) bool {
-       return (b == '-' || b == '_' ||
+       return b == '-' || b == '_' ||
                ('0' <= b && b <= '9') ||
                ('a' <= b && b <= 'z') ||
-               ('A' <= b && b <= 'Z'))
+               ('A' <= b && b <= 'Z')
 }
 
-func isWhiteSpace(b byte) bool {
+// isUrlExtraChar complements isTypeNameChar with extra characters that we 
allow
+// in URLs but not in type names. Note that '/' is not included so that it can
+// be treated specially.
+func isUrlExtraChar(b byte) bool {
        switch b {
-       case ' ', '\n', '\r', '\t':
+       case '.', '~', '!', '$', '&', '(', ')', '*', '+', ',', ';', '=':
                return true
        default:
                return false
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/vendor/google.golang.org/protobuf/internal/filedesc/desc.go 
new/vendor/google.golang.org/protobuf/internal/filedesc/desc.go
--- old/vendor/google.golang.org/protobuf/internal/filedesc/desc.go     
2025-12-24 17:06:29.000000000 +0100
+++ new/vendor/google.golang.org/protobuf/internal/filedesc/desc.go     
2025-12-31 15:18:43.000000000 +0100
@@ -32,6 +32,7 @@
        EditionProto3      Edition = 999
        Edition2023        Edition = 1000
        Edition2024        Edition = 1001
+       EditionUnstable    Edition = 9999
        EditionUnsupported Edition = 100000
 )
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go 
new/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go
--- old/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go        
2025-12-24 17:06:29.000000000 +0100
+++ new/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go        
2025-12-31 15:18:43.000000000 +0100
@@ -330,7 +330,6 @@
                                
md.L1.Extensions.List[extensionIdx].unmarshalFull(v, sb)
                                extensionIdx++
                        case genid.DescriptorProto_Options_field_number:
-                               md.unmarshalOptions(v)
                                rawOptions = appendOptions(rawOptions, v)
                        }
                default:
@@ -356,27 +355,6 @@
        md.L2.Options = 
md.L0.ParentFile.builder.optionsUnmarshaler(&descopts.Message, rawOptions)
 }
 
-func (md *Message) unmarshalOptions(b []byte) {
-       for len(b) > 0 {
-               num, typ, n := protowire.ConsumeTag(b)
-               b = b[n:]
-               switch typ {
-               case protowire.VarintType:
-                       v, m := protowire.ConsumeVarint(b)
-                       b = b[m:]
-                       switch num {
-                       case genid.MessageOptions_MapEntry_field_number:
-                               md.L1.IsMapEntry = protowire.DecodeBool(v)
-                       case 
genid.MessageOptions_MessageSetWireFormat_field_number:
-                               md.L1.IsMessageSet = protowire.DecodeBool(v)
-                       }
-               default:
-                       m := protowire.ConsumeFieldValue(num, typ, b)
-                       b = b[m:]
-               }
-       }
-}
-
 func unmarshalMessageReservedRange(b []byte) (r [2]protoreflect.FieldNumber) {
        for len(b) > 0 {
                num, typ, n := protowire.ConsumeTag(b)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go 
new/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go
--- old/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go      
2025-12-24 17:06:29.000000000 +0100
+++ new/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go      
2025-12-31 15:18:43.000000000 +0100
@@ -26,6 +26,7 @@
        Edition_EDITION_PROTO3_enum_value          = 999
        Edition_EDITION_2023_enum_value            = 1000
        Edition_EDITION_2024_enum_value            = 1001
+       Edition_EDITION_UNSTABLE_enum_value        = 9999
        Edition_EDITION_1_TEST_ONLY_enum_value     = 1
        Edition_EDITION_2_TEST_ONLY_enum_value     = 2
        Edition_EDITION_99997_TEST_ONLY_enum_value = 99997
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/vendor/google.golang.org/protobuf/internal/impl/codec_map.go 
new/vendor/google.golang.org/protobuf/internal/impl/codec_map.go
--- old/vendor/google.golang.org/protobuf/internal/impl/codec_map.go    
2025-12-24 17:06:29.000000000 +0100
+++ new/vendor/google.golang.org/protobuf/internal/impl/codec_map.go    
2025-12-31 15:18:43.000000000 +0100
@@ -113,6 +113,9 @@
 }
 
 func consumeMap(b []byte, mapv reflect.Value, wtyp protowire.Type, mapi 
*mapInfo, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err 
error) {
+       if opts.depth--; opts.depth < 0 {
+               return out, errRecursionDepth
+       }
        if wtyp != protowire.BytesType {
                return out, errUnknown
        }
@@ -170,6 +173,9 @@
 }
 
 func consumeMapOfMessage(b []byte, mapv reflect.Value, wtyp protowire.Type, 
mapi *mapInfo, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, 
err error) {
+       if opts.depth--; opts.depth < 0 {
+               return out, errRecursionDepth
+       }
        if wtyp != protowire.BytesType {
                return out, errUnknown
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/vendor/google.golang.org/protobuf/internal/impl/decode.go 
new/vendor/google.golang.org/protobuf/internal/impl/decode.go
--- old/vendor/google.golang.org/protobuf/internal/impl/decode.go       
2025-12-24 17:06:29.000000000 +0100
+++ new/vendor/google.golang.org/protobuf/internal/impl/decode.go       
2025-12-31 15:18:43.000000000 +0100
@@ -102,8 +102,7 @@
 
 func (mi *MessageInfo) unmarshalPointer(b []byte, p pointer, groupTag 
protowire.Number, opts unmarshalOptions) (out unmarshalOutput, err error) {
        mi.init()
-       opts.depth--
-       if opts.depth < 0 {
+       if opts.depth--; opts.depth < 0 {
                return out, errRecursionDepth
        }
        if flags.ProtoLegacy && mi.isMessageSet {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/vendor/google.golang.org/protobuf/internal/impl/validate.go 
new/vendor/google.golang.org/protobuf/internal/impl/validate.go
--- old/vendor/google.golang.org/protobuf/internal/impl/validate.go     
2025-12-24 17:06:29.000000000 +0100
+++ new/vendor/google.golang.org/protobuf/internal/impl/validate.go     
2025-12-31 15:18:43.000000000 +0100
@@ -68,9 +68,13 @@
        if in.Resolver == nil {
                in.Resolver = protoregistry.GlobalTypes
        }
+       if in.Depth == 0 {
+               in.Depth = protowire.DefaultRecursionLimit
+       }
        o, st := mi.validate(in.Buf, 0, unmarshalOptions{
                flags:    in.Flags,
                resolver: in.Resolver,
+               depth:    in.Depth,
        })
        if o.initialized {
                out.Flags |= protoiface.UnmarshalInitialized
@@ -257,6 +261,9 @@
                states[0].typ = validationTypeGroup
                states[0].endGroup = groupTag
        }
+       if opts.depth--; opts.depth < 0 {
+               return out, ValidationInvalid
+       }
        initialized := true
        start := len(b)
 State:
@@ -451,6 +458,13 @@
                                                mi:      vi.mi,
                                                tail:    b,
                                        })
+                                       if vi.typ == validationTypeMessage ||
+                                               vi.typ == validationTypeGroup ||
+                                               vi.typ == validationTypeMap {
+                                               if opts.depth--; opts.depth < 0 
{
+                                                       return out, 
ValidationInvalid
+                                               }
+                                       }
                                        b = v
                                        continue State
                                case validationTypeRepeatedVarint:
@@ -499,6 +513,9 @@
                                                mi:       vi.mi,
                                                endGroup: num,
                                        })
+                                       if opts.depth--; opts.depth < 0 {
+                                               return out, ValidationInvalid
+                                       }
                                        continue State
                                case flags.ProtoLegacy && vi.typ == 
validationTypeMessageSetItem:
                                        typeid, v, n, err := 
messageset.ConsumeFieldValue(b, false)
@@ -521,6 +538,13 @@
                                                        mi:   xvi.mi,
                                                        tail: b[n:],
                                                })
+                                               if xvi.typ == 
validationTypeMessage ||
+                                                       xvi.typ == 
validationTypeGroup ||
+                                                       xvi.typ == 
validationTypeMap {
+                                                       if opts.depth--; 
opts.depth < 0 {
+                                                               return out, 
ValidationInvalid
+                                                       }
+                                               }
                                                b = v
                                                continue State
                                        }
@@ -547,12 +571,14 @@
                switch st.typ {
                case validationTypeMessage, validationTypeGroup:
                        numRequiredFields = int(st.mi.numRequiredFields)
+                       opts.depth++
                case validationTypeMap:
                        // If this is a map field with a message value that 
contains
                        // required fields, require that the value be present.
                        if st.mi != nil && st.mi.numRequiredFields > 0 {
                                numRequiredFields = 1
                        }
+                       opts.depth++
                }
                // If there are more than 64 required fields, this check will
                // always fail and we will report that the message is 
potentially
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/vendor/google.golang.org/protobuf/internal/version/version.go 
new/vendor/google.golang.org/protobuf/internal/version/version.go
--- old/vendor/google.golang.org/protobuf/internal/version/version.go   
2025-12-24 17:06:29.000000000 +0100
+++ new/vendor/google.golang.org/protobuf/internal/version/version.go   
2025-12-31 15:18:43.000000000 +0100
@@ -52,7 +52,7 @@
 const (
        Major      = 1
        Minor      = 36
-       Patch      = 10
+       Patch      = 11
        PreRelease = ""
 )
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/google.golang.org/protobuf/proto/decode.go 
new/vendor/google.golang.org/protobuf/proto/decode.go
--- old/vendor/google.golang.org/protobuf/proto/decode.go       2025-12-24 
17:06:29.000000000 +0100
+++ new/vendor/google.golang.org/protobuf/proto/decode.go       2025-12-31 
15:18:43.000000000 +0100
@@ -121,9 +121,8 @@
 
                out, err = methods.Unmarshal(in)
        } else {
-               o.RecursionLimit--
-               if o.RecursionLimit < 0 {
-                       return out, errors.New("exceeded max recursion depth")
+               if o.RecursionLimit--; o.RecursionLimit < 0 {
+                       return out, errRecursionDepth
                }
                err = o.unmarshalMessageSlow(b, m)
        }
@@ -220,6 +219,9 @@
 }
 
 func (o UnmarshalOptions) unmarshalMap(b []byte, wtyp protowire.Type, mapv 
protoreflect.Map, fd protoreflect.FieldDescriptor) (n int, err error) {
+       if o.RecursionLimit--; o.RecursionLimit < 0 {
+               return 0, errRecursionDepth
+       }
        if wtyp != protowire.BytesType {
                return 0, errUnknown
        }
@@ -305,3 +307,5 @@
 var errUnknown = errors.New("BUG: internal error (unknown)")
 
 var errDecode = errors.New("cannot parse invalid wire-format data")
+
+var errRecursionDepth = errors.New("exceeded maximum recursion depth")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go 
new/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go
--- 
old/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go   
    2025-12-24 17:06:29.000000000 +0100
+++ 
new/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go   
    2025-12-31 15:18:43.000000000 +0100
@@ -172,13 +172,14 @@
 // ) to obtain a formatter capable of generating timestamps in this format.
 type Timestamp struct {
        state protoimpl.MessageState `protogen:"open.v1"`
-       // Represents seconds of UTC time since Unix epoch
-       // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
-       // 9999-12-31T23:59:59Z inclusive.
+       // Represents seconds of UTC time since Unix epoch 
1970-01-01T00:00:00Z. Must
+       // be between -315576000000 and 315576000000 inclusive (which 
corresponds to
+       // 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z).
        Seconds int64 `protobuf:"varint,1,opt,name=seconds,proto3" 
json:"seconds,omitempty"`
-       // Non-negative fractions of a second at nanosecond resolution. Negative
-       // second values with fractions must still have non-negative nanos 
values
-       // that count forward in time. Must be from 0 to 999,999,999
+       // Non-negative fractions of a second at nanosecond resolution. This 
field is
+       // the nanosecond portion of the duration, not an alternative to 
seconds.
+       // Negative second values with fractions must still have non-negative 
nanos
+       // values that count forward in time. Must be between 0 and 999,999,999
        // inclusive.
        Nanos         int32 `protobuf:"varint,2,opt,name=nanos,proto3" 
json:"nanos,omitempty"`
        unknownFields protoimpl.UnknownFields
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/modules.txt new/vendor/modules.txt
--- old/vendor/modules.txt      2025-12-24 17:06:29.000000000 +0100
+++ new/vendor/modules.txt      2025-12-31 15:18:43.000000000 +0100
@@ -1,4 +1,4 @@
-# code.forgejo.org/forgejo/actions-proto v0.5.3
+# code.forgejo.org/forgejo/actions-proto v0.6.0
 ## explicit; go 1.24.0
 code.forgejo.org/forgejo/actions-proto/ping/v1
 code.forgejo.org/forgejo/actions-proto/ping/v1/pingv1connect
@@ -504,7 +504,7 @@
 golang.org/x/time/rate
 # google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f
 ## explicit; go 1.19
-# google.golang.org/protobuf v1.36.10
+# google.golang.org/protobuf v1.36.11
 ## explicit; go 1.23
 google.golang.org/protobuf/encoding/protojson
 google.golang.org/protobuf/encoding/prototext

Reply via email to