@cat-factory/app 0.215.2 → 0.216.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -78,13 +78,21 @@ export function createUiResultViews() {
78
78
  // Likewise a coder parked on the implementation-fork choice or on undecided follow-up
79
79
  // items: those parks ride `step.approval` too, but the generic approve resolver refuses
80
80
  // them server-side, so the step must open the window that CAN resolve it.
81
+ const park = step ? dedicatedParkView(step, instance) : null
81
82
  const view = step?.consensus?.enabled
82
83
  ? 'consensus-session'
83
84
  : step?.prReview
84
85
  ? 'pr-review'
85
86
  : step
86
- ? (dedicatedParkView(step) ?? agentKindMeta(step.agentKind).resultView)
87
+ ? (park ?? agentKindMeta(step.agentKind).resultView)
87
88
  : undefined
89
+ // The PRE-TOKEN INPUT GATE is the one dedicated park with no window of its own: it is
90
+ // answered by an inline notice, which the generic step detail renders. Routing to the
91
+ // step's usual result view instead would open a window about work that has not run.
92
+ if (park === 'input-gate') {
93
+ stepDetail.value = { instanceId, stepIndex }
94
+ return
95
+ }
88
96
  if (view && instance) {
89
97
  // The brainstorm window is shared by both stages; carry which one from the step's kind.
90
98
  const stage =
@@ -14,6 +14,7 @@ const DEFAULTS: WorkspaceSettings = {
14
14
  artifactRetentionDays: 14,
15
15
  kaizenEnabled: true,
16
16
  delegateAgentsToRunnerPool: false,
17
+ inputGateMode: 'standard',
17
18
  reviewFrictionMode: 'off',
18
19
  reviewFrictionWarnCount: 3,
19
20
  reviewFrictionBlockCount: null,
@@ -80,6 +80,7 @@ export type {
80
80
  WorkspaceAccess,
81
81
  WorkspaceMember,
82
82
  TaskLimitMode,
83
+ InputGateMode,
83
84
  ReviewFrictionMode,
84
85
  WorkspaceSettings,
85
86
  WorkspaceMetadata,
@@ -0,0 +1,52 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import type { RunInputGate } from '@cat-factory/contracts'
3
+ import type { ExecutionInstance } from '~/types/execution'
4
+ import { inputGateNoticeFor } from './inputGate'
5
+
6
+ const run = (inputGate?: Partial<RunInputGate>): ExecutionInstance =>
7
+ ({
8
+ id: 'exe_1',
9
+ steps: [],
10
+ ...(inputGate
11
+ ? { inputGate: { mode: 'standard', issues: [], checkedAt: 1, ...inputGate } }
12
+ : {}),
13
+ }) as unknown as ExecutionInstance
14
+
15
+ const thin = [{ code: 'description_thin', severity: 'advisory' }] as RunInputGate['issues']
16
+ const missing = [{ code: 'description_missing', severity: 'blocking' }] as RunInputGate['issues']
17
+
18
+ describe('inputGateNoticeFor', () => {
19
+ it('shows the park, with the tone that carries the two ways out', () => {
20
+ expect(inputGateNoticeFor(run({ status: 'blocked', issues: missing }))?.tone).toBe('blocked')
21
+ })
22
+
23
+ it('keeps a waiver visible, because what was overruled explains the output', () => {
24
+ expect(inputGateNoticeFor(run({ status: 'overridden', issues: missing }))?.tone).toBe('waived')
25
+ })
26
+
27
+ // The regression this pins: advisory findings were recorded on the run and reported over the
28
+ // API while being invisible in the product, which left `advisory` MODE (whose entire purpose is
29
+ // "watch what the gate would have caught before turning it up") with nothing to watch.
30
+ it('shows advisory findings on a PASSED verdict, which is what advisory mode produces', () => {
31
+ const notice = inputGateNoticeFor(run({ status: 'passed', mode: 'advisory', issues: thin }))
32
+ expect(notice?.tone).toBe('advisory')
33
+ expect(notice?.gate.issues).toEqual(thin)
34
+ })
35
+
36
+ it('shows a standard-mode advisory too, which never parks but is still a finding', () => {
37
+ expect(inputGateNoticeFor(run({ status: 'passed', issues: thin }))?.tone).toBe('advisory')
38
+ })
39
+
40
+ it.each(['passed', 'off', 'not_applicable'] as const)(
41
+ 'says nothing about a %s verdict with no findings',
42
+ (status) => {
43
+ expect(inputGateNoticeFor(run({ status, issues: [] }))).toBeNull()
44
+ },
45
+ )
46
+
47
+ it('says nothing when the gate has not evaluated the run yet, or there is no run', () => {
48
+ expect(inputGateNoticeFor(run())).toBeNull()
49
+ expect(inputGateNoticeFor(null)).toBeNull()
50
+ expect(inputGateNoticeFor(undefined)).toBeNull()
51
+ })
52
+ })
@@ -0,0 +1,44 @@
1
+ // Which PRE-TOKEN INPUT GATE verdicts a run surfaces, and how they are presented.
2
+ //
3
+ // The gate records a verdict for EVERY disposition, including the ones where it did nothing, so
4
+ // "has a verdict" is not the same question as "has something to tell a human". This is the one
5
+ // place that answers the second one, because the run panel and the step-detail overlay both ask
6
+ // it and a per-component `status === 'blocked'` check is how they drift.
7
+
8
+ import type { ExecutionInstance } from '~/types/execution'
9
+ import type { RunInputGate } from '@cat-factory/contracts'
10
+
11
+ /**
12
+ * How a verdict reads to a human:
13
+ *
14
+ * - `blocked`: the run is parked, and the notice carries the two ways out.
15
+ * - `waived`: somebody read the blocking findings and ran anyway. Kept visible on the run that
16
+ * carries it, because what was overruled is part of what explains the output.
17
+ * - `advisory`: findings were recorded and nothing was parked. This is the whole point of
18
+ * `advisory` MODE ("watch what the gate would have caught before turning it up"), and it is
19
+ * also how `standard` mode reports a short description or a spike with no success criteria.
20
+ */
21
+ export type InputGateTone = 'blocked' | 'waived' | 'advisory'
22
+
23
+ /**
24
+ * The verdict a run should show, with the tone to show it in, or `null` when the gate has
25
+ * nothing to say.
26
+ *
27
+ * Nothing to say covers three real and different facts that happen to share a presentation:
28
+ * a verdict that has not been stamped yet, one the workspace turned `off`, and a clean `passed`.
29
+ * None of them is a message, so none of them earns a box on the panel. The distinction between
30
+ * them is preserved on the run and read by the API, not painted over here.
31
+ *
32
+ * Note what this deliberately does NOT gate on: a `passed` status. A `passed` verdict carrying
33
+ * advisories is exactly what advisory mode produces, and keying the notice off the status alone
34
+ * left every advisory finding recorded, reported over the API, and invisible in the product.
35
+ */
36
+ export function inputGateNoticeFor(
37
+ instance: ExecutionInstance | null | undefined,
38
+ ): { gate: RunInputGate; tone: InputGateTone } | null {
39
+ const gate = instance?.inputGate
40
+ if (!gate) return null
41
+ if (gate.status === 'blocked') return { gate, tone: 'blocked' }
42
+ if (gate.status === 'overridden') return { gate, tone: 'waived' }
43
+ return gate.issues.length > 0 ? { gate, tone: 'advisory' } : null
44
+ }
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from 'vitest'
2
- import type { PipelineStep } from '~/types/execution'
2
+ import type { ExecutionInstance, PipelineStep } from '~/types/execution'
3
3
  import { dedicatedParkView } from './pipelineRender'
4
4
 
5
5
  /** A minimal coder step; the predicate only reads approval/followUps/forkDecision. */
@@ -11,6 +11,14 @@ const step = (over: Partial<PipelineStep>): PipelineStep =>
11
11
  ...over,
12
12
  }) as PipelineStep
13
13
 
14
+ /**
15
+ * A run carrying no input-gate verdict: the ordinary case for every step-shaped park below.
16
+ * Passed explicitly because `dedicatedParkView` REQUIRES the run — the gate's park is a fact
17
+ * about the run rather than the step, so a call that omitted it would silently miss it.
18
+ */
19
+ const run = (over: Partial<ExecutionInstance> = {}): ExecutionInstance =>
20
+ ({ id: 'exe_1', steps: [], ...over }) as unknown as ExecutionInstance
21
+
14
22
  const followUps = (statuses: string[]) => ({
15
23
  enabled: true,
16
24
  items: statuses.map((status, i) => ({
@@ -32,13 +40,13 @@ describe('dedicatedParkView', () => {
32
40
  // proceed" rail.
33
41
  it('owns a follow-up park (pending approval + undecided items)', () => {
34
42
  expect(
35
- dedicatedParkView(step({ followUps: followUps(['pending', 'answered']) as never })),
43
+ dedicatedParkView(step({ followUps: followUps(['pending', 'answered']) as never }), run()),
36
44
  ).toBe('follow-ups')
37
45
  })
38
46
 
39
47
  it('does not claim a step whose follow-up items are all decided', () => {
40
48
  expect(
41
- dedicatedParkView(step({ followUps: followUps(['answered', 'dismissed']) as never })),
49
+ dedicatedParkView(step({ followUps: followUps(['answered', 'dismissed']) as never }), run()),
42
50
  ).toBeNull()
43
51
  })
44
52
 
@@ -47,26 +55,56 @@ describe('dedicatedParkView', () => {
47
55
  expect(
48
56
  dedicatedParkView(
49
57
  step({ state: 'working', approval: null, followUps: followUps(['pending']) as never }),
58
+ run(),
50
59
  ),
51
60
  ).toBeNull()
52
61
  })
53
62
 
54
63
  it('owns the fork park while awaiting a choice, and while a chat reply is in flight', () => {
55
- expect(dedicatedParkView(step({ forkDecision: { status: 'awaiting_choice' } as never }))).toBe(
56
- 'fork-decision',
57
- )
58
- expect(dedicatedParkView(step({ forkDecision: { status: 'answering' } as never }))).toBe(
64
+ expect(
65
+ dedicatedParkView(step({ forkDecision: { status: 'awaiting_choice' } as never }), run()),
66
+ ).toBe('fork-decision')
67
+ expect(dedicatedParkView(step({ forkDecision: { status: 'answering' } as never }), run())).toBe(
59
68
  'fork-decision',
60
69
  )
61
70
  })
62
71
 
63
72
  it('releases the step once the fork is resolved (chosen / single_path / skipped)', () => {
64
73
  for (const status of ['chosen', 'single_path', 'skipped', 'proposing']) {
65
- expect(dedicatedParkView(step({ forkDecision: { status } as never }))).toBeNull()
74
+ expect(dedicatedParkView(step({ forkDecision: { status } as never }), run())).toBeNull()
66
75
  }
67
76
  })
68
77
 
69
78
  it('leaves a plain approval park to the generic rail', () => {
70
- expect(dedicatedParkView(step({}))).toBeNull()
79
+ expect(dedicatedParkView(step({}), run())).toBeNull()
80
+ })
81
+
82
+ // The PRE-TOKEN INPUT GATE parks whatever step 0 happens to be and leaves nothing
83
+ // kind-specific on the step, so it is recognised off the RUN. The generic approve resolver
84
+ // refuses it server-side: approving it would mark the run's first working step done and skip
85
+ // the work the run exists to do.
86
+ it('owns a step whose park is the input gate, read off the run', () => {
87
+ const blocked = run({
88
+ inputGate: { status: 'blocked', mode: 'standard', issues: [], checkedAt: 1 },
89
+ } as never)
90
+ expect(dedicatedParkView(step({}), blocked)).toBe('input-gate')
91
+ })
92
+
93
+ it('releases the step once the gate is waived or passed', () => {
94
+ for (const status of ['overridden', 'passed', 'off', 'not_applicable']) {
95
+ const settled = run({
96
+ inputGate: { status, mode: 'standard', issues: [], checkedAt: 1 },
97
+ } as never)
98
+ expect(dedicatedParkView(step({}), settled)).toBeNull()
99
+ }
100
+ })
101
+
102
+ it('does not claim a step with no pending approval, whatever the gate says', () => {
103
+ // The gate's verdict alone must not turn an unparked step into a dedicated park: a run
104
+ // parked on the gate has exactly one step holding the approval.
105
+ const blocked = run({
106
+ inputGate: { status: 'blocked', mode: 'standard', issues: [], checkedAt: 1 },
107
+ } as never)
108
+ expect(dedicatedParkView(step({ approval: null, state: 'working' }), blocked)).toBeNull()
71
109
  })
72
110
  })
@@ -2,7 +2,7 @@
2
2
  // TaskPipelineMini, AgentStepDetail), so the "is this step still live?" logic stays
3
3
  // in one place rather than being re-derived as inline ternaries per component.
4
4
 
5
- import type { AgentState, PipelineStep } from '~/types/execution'
5
+ import type { AgentState, ExecutionInstance, PipelineStep } from '~/types/execution'
6
6
 
7
7
  /**
8
8
  * Visual state of a conditionally-run companion attached to a gate step (today the
@@ -141,8 +141,29 @@ export function isCompanionKind(kind: string): boolean {
141
141
  * server-side (`assertNotIterativeGate`), so every surface that offers a step's pending
142
142
  * approval must route these to their window instead of the generic "Approve & proceed"
143
143
  * rail — which would blink a 409 and resolve nothing.
144
+ *
145
+ * `input-gate` is the odd one out: it is resolved by an inline NOTICE rather than an overlay,
146
+ * because its remedy is to go and edit the task, which is a board action rather than something
147
+ * a modal could hold.
144
148
  */
145
- export function dedicatedParkView(step: PipelineStep): 'follow-ups' | 'fork-decision' | null {
149
+ export function dedicatedParkView(
150
+ step: PipelineStep,
151
+ instance: ExecutionInstance | null | undefined,
152
+ ): 'follow-ups' | 'fork-decision' | 'input-gate' | null {
153
+ // The PRE-TOKEN INPUT GATE parks whatever step 0 happens to be, so it leaves nothing on the
154
+ // STEP to recognise it by: its verdict is a fact about the RUN. Checked first, and off the
155
+ // instance: approving it generically would mark the run's first working step done and skip
156
+ // the work the run exists to do.
157
+ //
158
+ // `instance` is REQUIRED, and nullable rather than optional on purpose. Every park surface has
159
+ // the run in hand, and an optional parameter is how one of them silently stops passing it: the
160
+ // function would go on returning `null` for a gate-parked step, which each caller reads as
161
+ // "the generic approve rail applies" — the exact 409-blinking rail this exists to prevent.
162
+ // It still ACCEPTS an absent run (a store lookup that has not resolved), because that is a real
163
+ // state a caller has to be able to express; what it does not accept is not being asked.
164
+ if (instance?.inputGate?.status === 'blocked' && step.approval?.status === 'pending') {
165
+ return 'input-gate'
166
+ }
146
167
  // The fork park sits BEFORE the coder's build dispatch; `answering` (a chat turn in
147
168
  // flight) still belongs to the fork window, which renders the pending reply.
148
169
  const fork = step.forkDecision?.status
@@ -949,6 +949,16 @@
949
949
  "saveFailed": "Einstellungen konnten nicht gespeichert werden",
950
950
  "budgetSaved": "Budget gespeichert",
951
951
  "budgetSaveFailed": "Budget konnte nicht gespeichert werden"
952
+ },
953
+ "inputGate": {
954
+ "heading": "Eingabeprüfung vor dem Start eines Laufs",
955
+ "body": "Prüft den Wortlaut einer Aufgabe, bevor der erste Agentenschritt startet, damit eine unbearbeitbare Aufgabe ohne Verbrauch stoppt. Standard hält den Lauf an bei leerer oder Platzhalter-Beschreibung, einem Fehler ohne Reproduktionskontext oder einer Review-Aufgabe ohne Pull Request.",
956
+ "mode": "Modus",
957
+ "modes": {
958
+ "standard": "Standard (bei blockierender Lücke anhalten)",
959
+ "advisory": "Hinweis (erfassen, nie anhalten)",
960
+ "off": "Aus (Prüfung überspringen)"
961
+ }
952
962
  }
953
963
  },
954
964
  "localModelEndpoints": {
@@ -5142,6 +5152,8 @@
5142
5152
  "binary_output_service_invalid": "Dienst für Binärausgaben nicht auflösbar",
5143
5153
  "binary_output_generator_invalid": "Generator für Binärausgaben nicht auflösbar",
5144
5154
  "foundational_service_not_inherited": "Dieses Board hat den Dienst registriert",
5155
+ "input_gate_not_parked": "Nichts zu beantworten",
5156
+ "input_gate_parked": "Über die Eingabeprüfung der Aufgabe beantworten",
5145
5157
  "ticket_already_linked": "Dieses Ticket hat bereits eine Aufgabe",
5146
5158
  "dry_run_not_mergeable": "Probelauf kann nicht zusammengeführt werden"
5147
5159
  },
@@ -5176,6 +5188,8 @@
5176
5188
  "binary_output_service_invalid": "Ein Schritt, der Binärausgaben erzeugt, wählt einen Basisdienst aus, den der Katalog dieses Workspace nicht auflösen kann: Die ID ist unbekannt, oder der gewählte Speicherdienst trägt nicht die Fähigkeit asset-storage. Korrigieren Sie die Auswahl des Schritts oder registrieren Sie den Dienst und starten Sie erneut.",
5177
5189
  "binary_output_generator_invalid": "Ein Schritt, der Binärausgaben erzeugt, wählt eine generative Integration aus, die diese Installation nicht registriert, oder keine der gewählten Integrationen erzeugt einen Inhaltstyp, den der Schritt liefern muss. Generative Integrationen werden im Code der Installation registriert, nicht in diesem Workspace: registrieren Sie sie oder korrigieren Sie die Auswahl des Schritts und starten Sie erneut.",
5178
5190
  "foundational_service_not_inherited": "Abwählen gilt für einen vom Konto geerbten Dienst. Diese ID ist von diesem Board registriert, es gibt also nichts abzuwählen - lösche stattdessen den eigenen Eintrag des Boards.",
5191
+ "input_gate_not_parked": "Dieser Lauf wartet nicht mehr auf seine Eingabeprüfung. Möglicherweise hat sie jemand schon beantwortet oder der Lauf ist weitergelaufen.",
5192
+ "input_gate_parked": "Dieser Lauf wartet auf seine Eingabeprüfung, die über die Freigabe nicht beantwortet werden kann. Nutzen Sie den Hinweis am Lauf: Aufgabe ergänzen und erneut prüfen, oder trotzdem ausführen.",
5179
5193
  "ticket_already_linked": "Ein Ticket kann nur eine Aufgabe stützen. Es erneut zu verknüpfen würde der bestehenden Aufgabe genau den Kontext entziehen, mit dem sie angelegt wurde. Öffne stattdessen diese Aufgabe oder hebe die Verknüpfung des Tickets zuerst auf.",
5180
5194
  "dry_run_not_mergeable": "Dieser Pull Request stammt aus einem Probelauf und kann hier nicht zusammengeführt werden. Starte die Aufgabe erneut als echten Lauf, um einen Pull Request zu erzeugen, den dieser Arbeitsbereich zusammenführt."
5181
5195
  },
@@ -5414,6 +5428,51 @@
5414
5428
  "fail": "Lauf fehlgeschlagen"
5415
5429
  }
5416
5430
  },
5431
+ "inputGate": {
5432
+ "blockedTitle": "Diese Aufgabe braucht mehr Details, bevor sie laufen kann",
5433
+ "blockedBody": "Der Lauf wurde vor dem ersten Agentenschritt gestoppt, es wurde also nichts verbraucht. Ergänze das Fehlende an der Aufgabe und prüfe erneut.",
5434
+ "waivedTitle": "Trotz unvollständiger Aufgabe gestartet",
5435
+ "waivedBody": "Jemand hat entschieden, diese Aufgabe mit den unten offenen Lücken auszuführen. Sie bleiben als Teil der Laufhistorie erhalten.",
5436
+ "advisoryTitle": "Einige Lücken wurden notiert, der Lauf lief weiter",
5437
+ "advisoryBody": "Die Eingabeprüfung der Aufgabe hat diese Punkte beim Start gefunden. Keiner davon stoppt die Arbeit, daher wurde nichts angehalten. Wer sie ergänzt, erleichtert den nächsten Lauf.",
5438
+ "severity": {
5439
+ "blocking": "Blockierend",
5440
+ "advisory": "Hinweis"
5441
+ },
5442
+ "recheck": "Aufgabe erneut prüfen",
5443
+ "proceed": "Trotzdem ausführen",
5444
+ "recheckHint": "Die erneute Prüfung liest die Aufgabe im aktuellen Stand, bearbeite sie also zuerst.",
5445
+ "issue": {
5446
+ "description_missing": {
5447
+ "title": "Keine Beschreibung",
5448
+ "hint": "Der Titel benennt die Aufgabe, die Beschreibung ist die Arbeitsgrundlage des Agenten. Schreibe, was sich ändern soll und warum."
5449
+ },
5450
+ "description_placeholder": {
5451
+ "title": "Platzhalter-Beschreibung",
5452
+ "hint": "Die Beschreibung ist ein Platzhalter (\"TBD\", \"n/a\", \"fix it\") statt einer Beschreibung der Arbeit."
5453
+ },
5454
+ "description_thin": {
5455
+ "title": "Sehr kurze Beschreibung",
5456
+ "hint": "Wenige Worte legen selten fest, was \"fertig\" heißt. Mehr Details hier sparen später eine Rückfragerunde."
5457
+ },
5458
+ "reproduction_missing": {
5459
+ "title": "Kein Reproduktionskontext",
5460
+ "hint": "Ein Fehler ohne Schritte, ohne Soll-Ist-Vergleich und ohne Stacktrace lässt den Behebenden nicht erkennen, wann er behoben ist."
5461
+ },
5462
+ "review_target_missing": {
5463
+ "title": "Kein Pull Request zum Prüfen",
5464
+ "hint": "Eine Review-Aufgabe braucht die Nummer oder URL des Pull Requests, den sie lesen soll."
5465
+ },
5466
+ "success_criteria_missing": {
5467
+ "title": "Keine Erfolgskriterien",
5468
+ "hint": "Nenne die Frage, die der Spike beantwortet, oder wie ein gutes Ergebnis aussieht, damit die Zeitbox ein Ziel hat."
5469
+ },
5470
+ "unknown": {
5471
+ "title": "Unbekannter Befund",
5472
+ "hint": "Dieser Lauf hat eine Prüfung erfasst, die diese Version nicht mehr kennt. Sieh die Aufgabe von Hand durch, bevor du fortfährst."
5473
+ }
5474
+ }
5475
+ },
5417
5476
  "consensus": {
5418
5477
  "titlePrefix": "Konsens",
5419
5478
  "participantCount": "keine Teilnehmer | ein Teilnehmer | {count} Teilnehmer",
@@ -623,6 +623,8 @@
623
623
  "binary_output_service_invalid": "Binary output service can't be resolved",
624
624
  "binary_output_generator_invalid": "Binary output generator can't be resolved",
625
625
  "foundational_service_not_inherited": "This board registered that service",
626
+ "input_gate_not_parked": "Nothing to answer",
627
+ "input_gate_parked": "Answer it in the task's input check",
626
628
  "ticket_already_linked": "This issue already has a task",
627
629
  "dry_run_not_mergeable": "Dry run cannot be merged"
628
630
  },
@@ -660,6 +662,8 @@
660
662
  "binary_output_service_invalid": "A step that generates binary outputs selects a foundational service this workspace's catalog can't resolve: the id is unknown, or the chosen storage service doesn't carry the asset-storage capability. Fix the step's selection or register the service, then start again.",
661
663
  "binary_output_generator_invalid": "A step that generates binary outputs selects a generative integration this deployment doesn't register, or none of the selected integrations produces a content type the step must deliver. Generative integrations are registered in the deployment's code, not in this workspace: register it or fix the step's selection, then start again.",
662
664
  "foundational_service_not_inherited": "Opting out applies to a service inherited from the account. This id is registered by this board, so there is nothing to opt out of - delete the board's own entry instead.",
665
+ "input_gate_not_parked": "This run is not waiting on its input check any more. Someone may have answered it already, or the run has moved on.",
666
+ "input_gate_parked": "This run is parked on its input check, which the approval rail cannot answer. Use the notice on the run: fix the task and re-check, or run it anyway.",
663
667
  "ticket_already_linked": "An issue can back only one task, so linking it again would strip the existing task of the context it was created with. Open that task instead, or unlink the issue first.",
664
668
  "dry_run_not_mergeable": "This pull request came from a dry run, so it can't be merged from here. Start the task again as a live run to produce a pull request this workspace will merge."
665
669
  },
@@ -3398,6 +3402,16 @@
3398
3402
  "saveFailed": "Could not save settings",
3399
3403
  "budgetSaved": "Budget saved",
3400
3404
  "budgetSaveFailed": "Could not save budget"
3405
+ },
3406
+ "inputGate": {
3407
+ "heading": "Input check before a run starts",
3408
+ "body": "Check a task's own wording before the first agent step is dispatched, so a task nobody could act on stops without spending anything. Standard parks the run on an empty or placeholder description, a bug with no reproduction context, or a review task naming no pull request.",
3409
+ "mode": "Mode",
3410
+ "modes": {
3411
+ "standard": "Standard (park on a blocking gap)",
3412
+ "advisory": "Advisory (record, never park)",
3413
+ "off": "Off (skip the check)"
3414
+ }
3401
3415
  }
3402
3416
  },
3403
3417
  "localModelEndpoints": {
@@ -4748,6 +4762,51 @@
4748
4762
  "fail": "Failed the run"
4749
4763
  }
4750
4764
  },
4765
+ "inputGate": {
4766
+ "blockedTitle": "This task needs more detail before it can run",
4767
+ "blockedBody": "The run stopped before its first agent step, so nothing has been spent. Fill in what is missing on the task, then re-check.",
4768
+ "waivedTitle": "Started despite an incomplete task",
4769
+ "waivedBody": "Someone chose to run this task with the gaps below still open. They are kept here as part of the run's record.",
4770
+ "advisoryTitle": "Some gaps were noted, and the run continued",
4771
+ "advisoryBody": "The task's input check found these while starting the run. None of them stops the work, so nothing was parked; filling them in makes the next run easier to get right.",
4772
+ "severity": {
4773
+ "blocking": "Blocking",
4774
+ "advisory": "Advisory"
4775
+ },
4776
+ "recheck": "Re-check the task",
4777
+ "proceed": "Run anyway",
4778
+ "recheckHint": "Re-check reads the task as it stands now, so edit it first.",
4779
+ "issue": {
4780
+ "description_missing": {
4781
+ "title": "No description",
4782
+ "hint": "The title names the task; the description is what an agent works from. Say what should change and why."
4783
+ },
4784
+ "description_placeholder": {
4785
+ "title": "Placeholder description",
4786
+ "hint": "The description is a stand-in (\"TBD\", \"n/a\", \"fix it\") rather than a statement of the work."
4787
+ },
4788
+ "description_thin": {
4789
+ "title": "Very short description",
4790
+ "hint": "A few words rarely pin down what \"done\" means. More detail here saves a round of questions later."
4791
+ },
4792
+ "reproduction_missing": {
4793
+ "title": "No reproduction context",
4794
+ "hint": "A bug with no steps, no observed-versus-expected and no stack trace gives its fixer no way to tell when it is fixed."
4795
+ },
4796
+ "review_target_missing": {
4797
+ "title": "No pull request to review",
4798
+ "hint": "A review task needs the number or URL of the pull request it should read."
4799
+ },
4800
+ "success_criteria_missing": {
4801
+ "title": "No success criteria",
4802
+ "hint": "State the question the spike answers, or what a good outcome looks like, so the timebox has a target."
4803
+ },
4804
+ "unknown": {
4805
+ "title": "Unrecognised finding",
4806
+ "hint": "This run recorded a check this version no longer knows about. Review the task by hand before continuing."
4807
+ }
4808
+ }
4809
+ },
4751
4810
  "requirements": {
4752
4811
  "title": "Requirements review",
4753
4812
  "iteration": "Iteration {current} / {max}",
@@ -566,6 +566,8 @@
566
566
  "binary_output_service_invalid": "No se puede resolver el servicio de salidas binarias",
567
567
  "binary_output_generator_invalid": "No se puede resolver el generador de salidas binarias",
568
568
  "foundational_service_not_inherited": "Este tablero registró ese servicio",
569
+ "input_gate_not_parked": "Nada que responder",
570
+ "input_gate_parked": "Respóndelo en la comprobación de entrada de la tarea",
569
571
  "ticket_already_linked": "Esta incidencia ya tiene una tarea",
570
572
  "dry_run_not_mergeable": "Una ejecución de prueba no se puede fusionar"
571
573
  },
@@ -600,6 +602,8 @@
600
602
  "binary_output_service_invalid": "Un paso que genera salidas binarias selecciona un servicio fundacional que el catálogo de este espacio de trabajo no puede resolver: el identificador es desconocido, o el servicio de almacenamiento elegido no tiene la capacidad asset-storage. Corrige la selección del paso o registra el servicio y vuelve a iniciarlo.",
601
603
  "binary_output_generator_invalid": "Un paso que genera salidas binarias selecciona una integración generativa que esta instalación no registra, o ninguna de las integraciones seleccionadas produce un tipo de contenido que el paso debe entregar. Las integraciones generativas se registran en el código de la instalación, no en este espacio de trabajo: regístrala o corrige la selección del paso y vuelve a iniciar.",
602
604
  "foundational_service_not_inherited": "La exclusión se aplica a un servicio heredado de la cuenta. Este id está registrado por este tablero, así que no hay nada que excluir: elimina la entrada propia del tablero.",
605
+ "input_gate_not_parked": "Esta ejecución ya no espera su comprobación de entrada. Puede que alguien la haya respondido o que la ejecución haya avanzado.",
606
+ "input_gate_parked": "Esta ejecución está detenida en su comprobación de entrada, que la vía de aprobación no puede resolver. Usa el aviso de la ejecución: corrige la tarea y vuelve a comprobar, o ejecútala de todos modos.",
603
607
  "ticket_already_linked": "Una incidencia solo puede respaldar una tarea, así que volver a vincularla dejaría a la tarea existente sin el contexto con el que se creó. Abre esa tarea o desvincula antes la incidencia.",
604
608
  "dry_run_not_mergeable": "Esta pull request proviene de una ejecución de prueba, así que no se puede fusionar desde aquí. Vuelve a iniciar la tarea como ejecución real para producir una pull request que este espacio de trabajo sí fusionará."
605
609
  },
@@ -3159,6 +3163,16 @@
3159
3163
  "saveFailed": "No se pudo guardar la configuración",
3160
3164
  "budgetSaved": "Presupuesto guardado",
3161
3165
  "budgetSaveFailed": "No se pudo guardar el presupuesto"
3166
+ },
3167
+ "inputGate": {
3168
+ "heading": "Comprobación de entrada antes de iniciar una ejecución",
3169
+ "body": "Revisa la redacción de la tarea antes de lanzar el primer paso del agente, para que una tarea inabordable se detenga sin gastar nada. Estándar detiene la ejecución ante una descripción vacía o de relleno, un error sin contexto de reproducción, o una tarea de revisión sin pull request.",
3170
+ "mode": "Modo",
3171
+ "modes": {
3172
+ "standard": "Estándar (detener ante una carencia bloqueante)",
3173
+ "advisory": "Aviso (registrar, nunca detener)",
3174
+ "off": "Desactivado (omitir la comprobación)"
3175
+ }
3162
3176
  }
3163
3177
  },
3164
3178
  "localModelEndpoints": {
@@ -4543,6 +4557,51 @@
4543
4557
  "fail": "Ejecución fallida"
4544
4558
  }
4545
4559
  },
4560
+ "inputGate": {
4561
+ "blockedTitle": "Esta tarea necesita más detalle antes de ejecutarse",
4562
+ "blockedBody": "La ejecución se detuvo antes del primer paso del agente, así que no se ha gastado nada. Completa lo que falta en la tarea y vuelve a comprobar.",
4563
+ "waivedTitle": "Iniciada pese a una tarea incompleta",
4564
+ "waivedBody": "Alguien decidió ejecutar esta tarea con las carencias de abajo aún abiertas. Se conservan como parte del historial de la ejecución.",
4565
+ "advisoryTitle": "Se anotaron algunas carencias y la ejecución continuó",
4566
+ "advisoryBody": "La comprobación de entrada de la tarea encontró esto al iniciar la ejecución. Nada de ello detiene el trabajo, así que no se detuvo nada; completarlo facilita acertar en la próxima ejecución.",
4567
+ "severity": {
4568
+ "blocking": "Bloqueante",
4569
+ "advisory": "Aviso"
4570
+ },
4571
+ "recheck": "Volver a comprobar",
4572
+ "proceed": "Ejecutar igualmente",
4573
+ "recheckHint": "La comprobación lee la tarea tal como está ahora, así que edítala primero.",
4574
+ "issue": {
4575
+ "description_missing": {
4576
+ "title": "Sin descripción",
4577
+ "hint": "El título nombra la tarea; la descripción es con lo que trabaja un agente. Di qué debe cambiar y por qué."
4578
+ },
4579
+ "description_placeholder": {
4580
+ "title": "Descripción de relleno",
4581
+ "hint": "La descripción es un marcador (\"TBD\", \"n/a\", \"arreglarlo\") en vez de una descripción del trabajo."
4582
+ },
4583
+ "description_thin": {
4584
+ "title": "Descripción muy breve",
4585
+ "hint": "Unas pocas palabras rara vez definen qué significa \"terminado\". Más detalle aquí ahorra una ronda de preguntas después."
4586
+ },
4587
+ "reproduction_missing": {
4588
+ "title": "Sin contexto de reproducción",
4589
+ "hint": "Un error sin pasos, sin observado frente a esperado y sin traza no permite saber cuándo queda corregido."
4590
+ },
4591
+ "review_target_missing": {
4592
+ "title": "Sin pull request que revisar",
4593
+ "hint": "Una tarea de revisión necesita el número o la URL del pull request que debe leer."
4594
+ },
4595
+ "success_criteria_missing": {
4596
+ "title": "Sin criterios de éxito",
4597
+ "hint": "Indica la pregunta que responde el spike, o cómo sería un buen resultado, para que el tiempo acotado tenga un objetivo."
4598
+ },
4599
+ "unknown": {
4600
+ "title": "Hallazgo no reconocido",
4601
+ "hint": "Esta ejecución registró una comprobación que esta versión ya no conoce. Revisa la tarea a mano antes de continuar."
4602
+ }
4603
+ }
4604
+ },
4546
4605
  "requirements": {
4547
4606
  "title": "Revisión de requisitos",
4548
4607
  "iteration": "Iteración {current} / {max}",
@@ -566,6 +566,8 @@
566
566
  "binary_output_service_invalid": "Service des sorties binaires introuvable",
567
567
  "binary_output_generator_invalid": "Générateur de sorties binaires introuvable",
568
568
  "foundational_service_not_inherited": "Ce tableau a enregistré ce service",
569
+ "input_gate_not_parked": "Rien à répondre",
570
+ "input_gate_parked": "Répondez-y dans la vérification d'entrée de la tâche",
569
571
  "ticket_already_linked": "Ce ticket a déjà une tâche",
570
572
  "dry_run_not_mergeable": "Une exécution à blanc ne peut pas être fusionnée"
571
573
  },
@@ -600,6 +602,8 @@
600
602
  "binary_output_service_invalid": "Une étape qui génère des sorties binaires sélectionne un service fondamental que le catalogue de cet espace de travail ne peut pas résoudre : l’identifiant est inconnu, ou le service de stockage choisi ne porte pas la capacité asset-storage. Corrigez la sélection de l’étape ou enregistrez le service, puis relancez.",
601
603
  "binary_output_generator_invalid": "Une étape qui génère des sorties binaires sélectionne une intégration générative que ce déploiement n’enregistre pas, ou aucune des intégrations sélectionnées ne produit un type de contenu que l’étape doit livrer. Les intégrations génératives sont enregistrées dans le code du déploiement, pas dans cet espace de travail : enregistrez-la ou corrigez la sélection de l’étape, puis relancez.",
602
604
  "foundational_service_not_inherited": "L'écartement s'applique à un service hérité du compte. Cet identifiant est enregistré par ce tableau : il n'y a donc rien à écarter - supprimez plutôt l'entrée propre au tableau.",
605
+ "input_gate_not_parked": "Cette exécution n'attend plus sa vérification d'entrée. Quelqu'un y a peut-être déjà répondu, ou l'exécution a avancé.",
606
+ "input_gate_parked": "Cette exécution est en attente de sa vérification d'entrée, à laquelle la validation ne peut pas répondre. Utilisez l'avis sur l'exécution : corrigez la tâche et relancez la vérification, ou exécutez-la quand même.",
603
607
  "ticket_already_linked": "Un ticket ne peut alimenter qu'une seule tâche : le relier à nouveau priverait la tâche existante du contexte avec lequel elle a été créée. Ouvrez plutôt cette tâche, ou dissociez d'abord le ticket.",
604
608
  "dry_run_not_mergeable": "Cette pull request provient d'une exécution à blanc et ne peut pas être fusionnée ici. Relancez la tâche en exécution réelle pour produire une pull request que cet espace de travail fusionnera."
605
609
  },
@@ -3159,6 +3163,16 @@
3159
3163
  "saveFailed": "Impossible d'enregistrer les paramètres",
3160
3164
  "budgetSaved": "Budget enregistré",
3161
3165
  "budgetSaveFailed": "Impossible d'enregistrer le budget"
3166
+ },
3167
+ "inputGate": {
3168
+ "heading": "Vérification des entrées avant le démarrage d'une exécution",
3169
+ "body": "Contrôle la formulation d'une tâche avant l'envoi du premier pas de l'agent, pour qu'une tâche inexploitable s'arrête sans rien dépenser. Standard met l'exécution en pause sur une description vide ou de remplissage, un bogue sans contexte de reproduction, ou une tâche de relecture sans pull request.",
3170
+ "mode": "Mode",
3171
+ "modes": {
3172
+ "standard": "Standard (mettre en pause sur un manque bloquant)",
3173
+ "advisory": "Indicatif (enregistrer, jamais mettre en pause)",
3174
+ "off": "Désactivé (ignorer la vérification)"
3175
+ }
3162
3176
  }
3163
3177
  },
3164
3178
  "localModelEndpoints": {
@@ -4543,6 +4557,51 @@
4543
4557
  "fail": "Exécution échouée"
4544
4558
  }
4545
4559
  },
4560
+ "inputGate": {
4561
+ "blockedTitle": "Cette tâche a besoin de plus de détails avant de s'exécuter",
4562
+ "blockedBody": "L'exécution s'est arrêtée avant le premier pas de l'agent, rien n'a donc été dépensé. Complétez ce qui manque sur la tâche, puis relancez la vérification.",
4563
+ "waivedTitle": "Démarrée malgré une tâche incomplète",
4564
+ "waivedBody": "Quelqu'un a choisi d'exécuter cette tâche avec les manques ci-dessous encore ouverts. Ils sont conservés dans l'historique de l'exécution.",
4565
+ "advisoryTitle": "Quelques manques relevés, l'exécution a continué",
4566
+ "advisoryBody": "La vérification d'entrée de la tâche les a repérés au démarrage. Aucun n'empêche le travail, rien n'a donc été mis en attente : les compléter facilitera la prochaine exécution.",
4567
+ "severity": {
4568
+ "blocking": "Bloquant",
4569
+ "advisory": "Indicatif"
4570
+ },
4571
+ "recheck": "Revérifier la tâche",
4572
+ "proceed": "Exécuter quand même",
4573
+ "recheckHint": "La vérification lit la tâche telle qu'elle est maintenant, modifiez-la d'abord.",
4574
+ "issue": {
4575
+ "description_missing": {
4576
+ "title": "Aucune description",
4577
+ "hint": "Le titre nomme la tâche ; la description est ce sur quoi un agent travaille. Dites ce qui doit changer et pourquoi."
4578
+ },
4579
+ "description_placeholder": {
4580
+ "title": "Description de remplissage",
4581
+ "hint": "La description est un substitut (\"TBD\", \"n/a\", \"à corriger\") plutôt qu'un énoncé du travail."
4582
+ },
4583
+ "description_thin": {
4584
+ "title": "Description très courte",
4585
+ "hint": "Quelques mots définissent rarement ce que \"terminé\" veut dire. Plus de détail ici évite un aller-retour de questions."
4586
+ },
4587
+ "reproduction_missing": {
4588
+ "title": "Aucun contexte de reproduction",
4589
+ "hint": "Un bogue sans étapes, sans observé-contre-attendu et sans trace d'appels ne permet pas de savoir quand il est corrigé."
4590
+ },
4591
+ "review_target_missing": {
4592
+ "title": "Aucune pull request à relire",
4593
+ "hint": "Une tâche de relecture a besoin du numéro ou de l'URL de la pull request à lire."
4594
+ },
4595
+ "success_criteria_missing": {
4596
+ "title": "Aucun critère de réussite",
4597
+ "hint": "Indiquez la question à laquelle le spike répond, ou ce qu'est un bon résultat, pour donner une cible au temps imparti."
4598
+ },
4599
+ "unknown": {
4600
+ "title": "Constat non reconnu",
4601
+ "hint": "Cette exécution a enregistré une vérification que cette version ne connaît plus. Relisez la tâche à la main avant de continuer."
4602
+ }
4603
+ }
4604
+ },
4546
4605
  "requirements": {
4547
4606
  "title": "Revue des exigences",
4548
4607
  "iteration": "Itération {current} / {max}",