Skip to content

Commit 4912fdb

Browse files
committed
fix: align host parity with host-owned permissions
1 parent 5f40bc1 commit 4912fdb

11 files changed

Lines changed: 97 additions & 85 deletions

‎hooks/_runtime/evidence/codex-skill-route-pass.v1.json‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,24 @@
55
"hostVariant": "codex-cli/exec-user-global-local-stdio",
66
"testedVersion": "codex-cli 0.145.0 / source candidate 1.19.2",
77
"protocol": "MCP 2024-11-05",
8-
"runtimeContractDigest": "1233b302908e8080ae6c2c15e21c91c0b002f7f559181a0b30713a322d65d45b",
8+
"runtimeContractDigest": "bb67159b9e56e6d411f766e17eb7f844b961ad4586c418150e32015528760214",
99
"hostAdapterDigest": "01b3456fe5de1a8425c4a63d40cecc16bbbf83b51bfeca794667ed772ea2a23e",
10-
"sourceEvidenceDigest": "aa3257b9b24713bb1fdfe02c9634e38a22b83d5b1d80e631cb332d3483959928",
10+
"sourceEvidenceDigest": "669277b85ffe34f355415ff8aac513c5c5a013993043a5ef2f96141fafeaf165",
1111
"sourceProbe": {
1212
"schemaVersion": "SkillRouteS15EvidenceV1",
13-
"probeRunId": "s15-codex-probe-17b57917-51e0-4ea2-971d-50e17ccf5f85",
13+
"probeRunId": "s15-codex-probe-6444b046-0df9-4805-a836-318ad2a3083e",
1414
"authorizationSource": "isolated-probe-authority",
1515
"contextSource": "host-hooks",
1616
"observationMode": "hook-post-history",
1717
"receiptStatus": "relevant-complete",
18-
"completedAt": "2026-08-27T13:08:42.627Z"
18+
"completedAt": "2026-08-27T14:32:52.422Z"
1919
},
2020
"runtimeBinding": {
2121
"source": "isolated-source-candidate",
22-
"expectedDigest": "1233b302908e8080ae6c2c15e21c91c0b002f7f559181a0b30713a322d65d45b",
23-
"generationDigest": "1233b302908e8080ae6c2c15e21c91c0b002f7f559181a0b30713a322d65d45b",
24-
"modeReceiptDigest": "1233b302908e8080ae6c2c15e21c91c0b002f7f559181a0b30713a322d65d45b",
25-
"routeEnvelopeDigest": "1233b302908e8080ae6c2c15e21c91c0b002f7f559181a0b30713a322d65d45b"
22+
"expectedDigest": "bb67159b9e56e6d411f766e17eb7f844b961ad4586c418150e32015528760214",
23+
"generationDigest": "bb67159b9e56e6d411f766e17eb7f844b961ad4586c418150e32015528760214",
24+
"modeReceiptDigest": "bb67159b9e56e6d411f766e17eb7f844b961ad4586c418150e32015528760214",
25+
"routeEnvelopeDigest": "bb67159b9e56e6d411f766e17eb7f844b961ad4586c418150e32015528760214"
2626
},
2727
"probe": {
2828
"schemaVersion": "SkillRouteProbeSummaryV1",

‎hooks/_runtime/host-skill-route-capabilities.v1.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313
"status": "PASS",
1414
"testedVersion": "codex-cli 0.145.0 / source candidate 1.19.2",
1515
"protocol": "MCP 2024-11-05",
16-
"runtimeContractDigest": "1233b302908e8080ae6c2c15e21c91c0b002f7f559181a0b30713a322d65d45b",
16+
"runtimeContractDigest": "bb67159b9e56e6d411f766e17eb7f844b961ad4586c418150e32015528760214",
1717
"hostAdapterDigest": "01b3456fe5de1a8425c4a63d40cecc16bbbf83b51bfeca794667ed772ea2a23e",
1818
"evidenceRef": "hooks/_runtime/evidence/codex-skill-route-pass.v1.json",
19-
"evidenceDigest": "dc16e87091ef193ca2ff63ff65b7841f4973b331a3836d353b2733bdea3e7e6c",
19+
"evidenceDigest": "f5bcb910337b20ba820fc4716dcd9f086230604f7e2893ead08ac4bd1f68dc7a",
2020
"entrySurface": "codex exec --ephemeral",
2121
"bootstrapDelivery": "stable user-global Hook launcher UserPromptSubmit or profile_context_plan fallback",
2222
"defaultEligible": true

‎hooks/_runtime/workflow-root-registry.v1.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
},
1212
{
1313
"ref": "scripts/lib/host-parity-scorecard.js",
14-
"digest": "4f719c98f0c9116ffdebfae582e1603495adb2649612b2ea18338764267895e4"
14+
"digest": "369ddd5e734a0da918406ddaeb30f97ff77d5dfe5eb67fe9125099d6cdf160af"
1515
}
1616
],
17-
"sourceDigest": "f27ed972829e0b988246fbe8955a9b5ab6a0d9ab92a9d9ed88e3e867b43d4397",
17+
"sourceDigest": "dec93e76b16fb4d161a1583ec22f5fe1159f185f1793c56c7793900bf5f6a20a",
1818
"baseBundles": {
1919
"chat": [],
2020
"resume": [

‎hooks/_runtime/workflow-root-registry.v2.json‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,10 +1089,10 @@
10891089
},
10901090
{
10911091
"ref": "scripts/lib/host-parity-scorecard.js",
1092-
"digest": "4f719c98f0c9116ffdebfae582e1603495adb2649612b2ea18338764267895e4"
1092+
"digest": "369ddd5e734a0da918406ddaeb30f97ff77d5dfe5eb67fe9125099d6cdf160af"
10931093
}
10941094
],
1095-
"sourceDigest": "a9e9a9ec3920dc93a6e18812d9cb15c4fc4ebbc14accc6dfe55fd5163bda014d",
1095+
"sourceDigest": "46e53587dd323cdc3c2c7b31b8ebaabfef63138b6d3d767473fcb554807484e6",
10961096
"routeRevision": "cd86a6045a9fc8cd72b094254bc68bef4210e0bb4639a7c2d755f8368aa3310b",
1097-
"registryDigest": "d4374e29da84a8dbd6688162406995401ccb9da7433be4a891628be224e77dd2"
1097+
"registryDigest": "dfc208ce79d21b76cb85a19db58f37d89d071ce1a884f4ff3c821a31295247a8"
10981098
}

‎scripts/lib/cli-maintenance-commands.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1006,7 +1006,7 @@ function buildCliMaintenanceCommands(ctx) {
10061006
console.log(c.dim(' GrokTurnChecklist: PC0~PC7 → Skill bundle → work → report+memory (see host-parity-grok.md)'))
10071007
console.log()
10081008
} else if (hostParity && hostParity.hardReady) {
1009-
console.log(c.dim(' Grok HostParity: PreTool deny + path-observable ready. Still cannot claim UserPromptSubmit inject or Stop hard-block.'))
1009+
console.log(c.dim(' Grok HostParity: host-owned PreTool operations + path-observable ready. Still cannot claim UserPromptSubmit inject or Stop hard-block.'))
10101010
console.log(c.dim(' Prefer `devcodex grok` in child Git projects for Full kernel evidence.'))
10111011
console.log(c.dim(' GrokTurnChecklist + Intent→Skill bundle still required (passive host has no inject).'))
10121012
console.log()

‎scripts/lib/host-enforcement-matrix.js‎

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
/**
44
* HostEnforcementMatrixV1 — canonical hosts, process-enforcement truth source.
5-
* hard-deny | stop-block | honesty-only | N/A+platform-limit
5+
* host-owned-advisory | stop-block | honesty-only | N/A+platform-limit
66
*/
77

88
const HOST_ENFORCEMENT_MATRIX_V1 = Object.freeze({
@@ -11,42 +11,44 @@ const HOST_ENFORCEMENT_MATRIX_V1 = Object.freeze({
1111
copilot: Object.freeze({
1212
id: 'copilot',
1313
label: 'GitHub Copilot',
14-
preToolMutationNoCp2: 'hard-deny',
15-
preToolNotes: 'CLI hooks when configured; IDE may be instruction-fallback → honesty-only for some surfaces',
14+
preToolMutationNoCp2: 'host-owned-advisory',
15+
preToolNotes: 'Operation permission belongs to the active host; DevCodex may emit advisory context only',
1616
stopCompletion: 'stop-block',
1717
upsInject: 'N/A+platform-limit',
1818
upsNotes: 'CLI vs IDE ceilings differ; never claim full IDE hook parity'
1919
}),
2020
claude: Object.freeze({
2121
id: 'claude',
2222
label: 'Claude Code',
23-
preToolMutationNoCp2: 'hard-deny',
23+
preToolMutationNoCp2: 'host-owned-advisory',
24+
preToolNotes: 'Operation permission belongs to Claude Code; DevCodex does not emit allow/deny/ask',
2425
stopCompletion: 'stop-block',
2526
upsInject: 'hard-deny',
2627
upsNotes: 'additionalContext / inject when hook-enforced'
2728
}),
2829
codex: Object.freeze({
2930
id: 'codex',
3031
label: 'Codex',
31-
preToolMutationNoCp2: 'hard-deny',
32+
preToolMutationNoCp2: 'host-owned-advisory',
33+
preToolNotes: 'Operation permission belongs to Codex; DevCodex does not emit allow/deny/ask',
3234
stopCompletion: 'stop-block',
3335
upsInject: 'hard-deny',
3436
upsNotes: 'systemMessage + additionalContext when configured'
3537
}),
3638
gemini: Object.freeze({
3739
id: 'gemini',
3840
label: 'Gemini CLI',
39-
preToolMutationNoCp2: 'hard-deny',
40-
preToolNotes: 'When Gemini hooks/settings support PreTool deny; otherwise document honesty-only in runtime probe',
41+
preToolMutationNoCp2: 'host-owned-advisory',
42+
preToolNotes: 'Operation permission belongs to Gemini; DevCodex does not emit allow/deny/ask',
4143
stopCompletion: 'stop-block',
4244
upsInject: 'N/A+platform-limit',
4345
upsNotes: 'Do not claim UPS inject without host evidence'
4446
}),
4547
grok: Object.freeze({
4648
id: 'grok',
4749
label: 'Grok Build',
48-
preToolMutationNoCp2: 'hard-deny',
49-
preToolNotes: 'decision:deny supported on PreToolUse',
50+
preToolMutationNoCp2: 'host-owned-advisory',
51+
preToolNotes: 'Operation permission belongs to Grok; DevCodex returns no PreTool decision',
5052
stopCompletion: 'stop-block',
5153
stopNotes: 'conditional decision:block when lastAssistantMessage present',
5254
upsInject: 'N/A+platform-limit',
@@ -55,8 +57,8 @@ const HOST_ENFORCEMENT_MATRIX_V1 = Object.freeze({
5557
cursor: Object.freeze({
5658
id: 'cursor',
5759
label: 'Cursor Beta',
58-
preToolMutationNoCp2: 'hard-deny',
59-
preToolNotes: 'Local IDE/CLI/Headless user hooks support preToolUse allow/deny with failClosed; Cloud user hooks are unavailable',
60+
preToolMutationNoCp2: 'host-owned-advisory',
61+
preToolNotes: 'Operation permission belongs to Cursor; Cloud user hooks are unavailable',
6062
stopCompletion: 'stop-followup',
6163
stopNotes: 'Local stop uses bounded followup_message with loop_limit=5; not a Cloud claim',
6264
upsInject: 'session-start-only',

‎scripts/lib/host-parity-scorecard.js‎

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
/**
44
* HostParityScorecardV1 — Grok vs Codex capability honesty for doctor/status.
5-
* Full ≠ Codex API isomorphism; Full = launcher rules bind + PreTool deny path + path-observable.
5+
* Full ≠ Codex API isomorphism; Full = launcher rules bind + host-owned operation path + path-observable.
66
* PF-165: GrokTurnChecklist + Intent→Skill bundle + doctor repairSteps (no parallel system).
77
*/
88

@@ -80,10 +80,10 @@ function buildCheckRepairCatalog(guidance) {
8080
command,
8181
detail: 'Reconcile stale receipts or managed configuration drift without reporting present runtime files as missing.'
8282
},
83-
denyAdapterContract: {
84-
check: 'denyAdapterContract',
83+
operationAdvisoryContract: {
84+
check: 'operationAdvisoryContract',
8585
command,
86-
detail: 'lifecycle-host-adapters must export adaptGrokOutput and decision:deny mapping.'
86+
detail: 'lifecycle-host-adapters must export adaptGrokOutput and leave operation decisions to the host.'
8787
},
8888
pathObservableCapability: {
8989
check: 'pathObservableCapability',
@@ -327,19 +327,19 @@ function fileExists(filePath) {
327327
}
328328
}
329329

330-
function readAdapterDenyContract(adapterPath) {
330+
function readAdapterOperationContract(adapterPath) {
331331
if (!fileExists(adapterPath)) {
332-
return { present: false, hasAdaptGrok: false, hasDenyDecision: false }
332+
return { present: false, hasAdaptGrok: false, operationPermissionHostOwned: false }
333333
}
334334
try {
335335
const text = fs.readFileSync(adapterPath, 'utf8')
336336
return {
337337
present: true,
338338
hasAdaptGrok: /function adaptGrokOutput|adaptGrokOutput\s*\(/.test(text),
339-
hasDenyDecision: /decision:\s*['"]deny['"]/.test(text)
339+
operationPermissionHostOwned: /isOperationPermissionEvent\(originalEvent\)\)\s*return\s*\{\s*\}/.test(text)
340340
}
341341
} catch {
342-
return { present: false, hasAdaptGrok: false, hasDenyDecision: false }
342+
return { present: false, hasAdaptGrok: false, operationPermissionHostOwned: false }
343343
}
344344
}
345345

@@ -408,7 +408,7 @@ function evaluateGrokHostParity(input = {}) {
408408
const pluginRoot = grokTarget.files.plugin
409409
const codexAdapter = path.join(codexRuntime, 'hooks', '_runtime', 'lifecycle-host-adapters.cjs')
410410
const codexBootstrap = path.join(codexRuntime, 'hooks', '_runtime', 'lifecycle-bootstrap-state.cjs')
411-
const deny = readAdapterDenyContract(codexAdapter)
411+
const operationAdapter = readAdapterOperationContract(codexAdapter)
412412
const bootstrap = readBootstrapCapability(codexBootstrap)
413413

414414
const hasGlobalKernel = input.hasGlobalKernel !== undefined
@@ -440,7 +440,11 @@ function evaluateGrokHostParity(input = {}) {
440440
codexAdapterContractReady,
441441
grokAdapterContractReady,
442442
managedConfigCurrent,
443-
denyAdapterContract: Boolean(deny.present && deny.hasAdaptGrok && deny.hasDenyDecision),
443+
operationAdvisoryContract: Boolean(
444+
operationAdapter.present &&
445+
operationAdapter.hasAdaptGrok &&
446+
operationAdapter.operationPermissionHostOwned
447+
),
444448
pathObservableCapability: Boolean(bootstrap.present && bootstrap.grokPathObservable),
445449
globalGrokPluginInstalled: hasGlobalGrokPlugin,
446450
globalGrokPluginConfigured: hasGlobalGrokConfig
@@ -451,7 +455,7 @@ function evaluateGrokHostParity(input = {}) {
451455
&& checks.codexAdapterContractReady
452456
&& checks.grokAdapterContractReady
453457
&& checks.managedConfigCurrent
454-
&& checks.denyAdapterContract
458+
&& checks.operationAdvisoryContract
455459
&& checks.pathObservableCapability
456460
&& checks.globalGrokPluginInstalled
457461
&& checks.globalGrokPluginConfigured
@@ -500,7 +504,7 @@ function evaluateGrokHostParity(input = {}) {
500504
codexBootstrap: fileExists(codexBootstrap) ? codexBootstrap : null,
501505
pluginRoot: hasGlobalGrokPlugin ? pluginRoot : null,
502506
scope: 'user-global',
503-
deny,
507+
operationAdapter,
504508
bootstrap,
505509
physicalPresence: {
506510
globalKernelFilesPresent: hasGlobalKernel,
@@ -519,7 +523,7 @@ function evaluateGrokHostParity(input = {}) {
519523
recommendedEntry,
520524
cannotClaim,
521525
userVisibleSummary: hardReady
522-
? 'Grok HostParity: full-capable (PreTool deny + path-observable + kernel). Use `devcodex grok` for Full session evidence. Inject/Stop still Partial. Follow GrokTurnChecklist + Intent→Skill bundle.'
526+
? 'Grok HostParity: full-capable (host-owned PreTool operations + path-observable + kernel). Use `devcodex grok` for Full session evidence. Inject/Stop still Partial. Follow GrokTurnChecklist + Intent→Skill bundle.'
523527
: `Grok HostParity: partial — failed: ${failedChecks.join(', ') || 'unknown'}. Fix: ${repairPreview || fallbackFix}. Then doctor --json hostParity.repairSteps.`
524528
}
525529

@@ -596,7 +600,7 @@ module.exports = {
596600
classifyGrokTurnOmissionSample,
597601
classifyWorkspaceRootScanSample,
598602
classifyTtfvOmissionSample,
599-
readAdapterDenyContract,
603+
readAdapterOperationContract,
600604
readBootstrapCapability
601605
}
602606

‎scripts/test-host-parity-remaining.js‎

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33

44
/**
55
* Remaining HostParity deliverables smoke:
6-
* - cross-host hard-block matrix not weakened
7-
* - gemini/grok deny shapes
6+
* - cross-host operation authority remains host-owned
7+
* - operation outputs carry no DevCodex permission decision
88
* - SessionStart stamp
99
* - entry check compose
10-
* - lifecycle PreToolUse deny path via host adapter (fixture)
10+
* - lifecycle PreToolUse advisory path via host adapter (fixture)
1111
*/
1212

1313
const assert = require('assert')
@@ -37,46 +37,45 @@ process.once('exit', cleanupTempFixtures)
3737

3838
const hookOut = buildLifecycleHookOutput({ env: {}, enforcementMode: 'safety-only' })
3939

40-
// Cross-host: Codex/Claude not weakened
40+
// Cross-host: operation permission always belongs to the host.
4141
assert.strictEqual(hookOut.eventSupportsHardBlock('codex', 'UserPromptSubmit'), true)
42-
assert.strictEqual(hookOut.eventSupportsHardBlock('codex', 'PreToolUse'), true)
42+
assert.strictEqual(hookOut.eventSupportsHardBlock('codex', 'PreToolUse'), false)
4343
assert.strictEqual(hookOut.eventSupportsHardBlock('codex', 'Stop'), true)
4444
assert.strictEqual(hookOut.eventSupportsHardBlock('claude', 'UserPromptSubmit'), true)
4545
assert.strictEqual(hookOut.eventSupportsHardBlock('claude', 'Stop'), true)
46-
assert.strictEqual(hookOut.eventSupportsHardBlock('claude', 'PreToolUse'), true)
47-
// Grok: PreTool + conditional Stop; UPS remains non-hard
48-
assert.strictEqual(hookOut.eventSupportsHardBlock('grok', 'PreToolUse'), true)
46+
assert.strictEqual(hookOut.eventSupportsHardBlock('claude', 'PreToolUse'), false)
47+
// Grok: PreTool is host-owned; conditional Stop remains a workflow-validity surface.
48+
assert.strictEqual(hookOut.eventSupportsHardBlock('grok', 'PreToolUse'), false)
4949
assert.strictEqual(hookOut.eventSupportsHardBlock('grok', 'UserPromptSubmit'), false)
5050
assert.strictEqual(hookOut.eventSupportsHardBlock('grok', 'Stop'), true)
5151
// Copilot / instruction-fallback
52-
assert.strictEqual(hookOut.eventSupportsHardBlock('copilot', 'PreToolUse'), true)
52+
assert.strictEqual(hookOut.eventSupportsHardBlock('copilot', 'PreToolUse'), false)
5353
assert.strictEqual(hookOut.eventSupportsHardBlock('jetbrains-copilot', 'PreToolUse'), false)
5454

55-
// Gemini adapter still converts Claude permission shape
55+
// Operation adapters strip legacy permission shapes for every host.
5656
const geminiDeny = adaptHostOutput('gemini', 'BeforeTool', {
5757
hookSpecificOutput: {
5858
permissionDecision: 'deny',
5959
permissionDecisionReason: 'gemini-danger'
6060
}
6161
})
62-
assert.strictEqual(geminiDeny.decision, 'deny')
63-
assert.strictEqual(geminiDeny.reason, 'gemini-danger')
62+
assert.deepStrictEqual(geminiDeny, {})
6463

65-
// Grok deny official shape
64+
// Grok operation output is also empty.
6665
const grokDeny = adaptHostOutput('grok', 'PreToolUse', {
6766
hookSpecificOutput: {
6867
permissionDecision: 'deny',
6968
permissionDecisionReason: 'workflow-invalid'
7069
}
7170
})
72-
assert.deepStrictEqual(grokDeny, { decision: 'deny', reason: 'workflow-invalid' })
71+
assert.deepStrictEqual(grokDeny, {})
7372

74-
// Codex path through adaptHostOutput is uncommon; ensure non-grok non-gemini does not strip to only decision
73+
// Codex may carry advisory context, but never a permission decision.
7574
const codexOut = adaptHostOutput('codex', 'PreToolUse', {
7675
continue: true,
7776
hookSpecificOutput: { permissionDecision: 'deny', permissionDecisionReason: 'c' }
7877
})
79-
assert.strictEqual(codexOut.hookSpecificOutput.permissionDecision, 'deny')
78+
assert.ok(!codexOut.hookSpecificOutput?.permissionDecision)
8079

8180
function readIfExists(file) {
8281
return fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : null

‎scripts/test-host-parity-scorecard.js‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ assert.strictEqual(ready.hardReady, true)
8989
assert.strictEqual(ready.tier, 'full-capable')
9090
assert.strictEqual(ready.failedChecks.length, 0)
9191
assert.ok(!ready.repairSteps.some((step) => step.status === 'failed'))
92-
assert.ok(ready.checks.denyAdapterContract)
92+
assert.ok(ready.checks.operationAdvisoryContract)
9393
assert.ok(ready.checks.pathObservableCapability)
9494
assert.ok(Array.isArray(ready.cannotClaim) && ready.cannotClaim.length >= 3)
9595
assert.strictEqual(MIN_CANNOT_CLAIM.length, 4)
@@ -172,7 +172,7 @@ assert.match(formatGrokTurnChecklistMarkdown(), /scan-hygiene|ttfv-first-deliver
172172
const regOnly = buildGrokRepairSteps({
173173
globalKernelAgentsMd: true,
174174
globalCodexLifecycleReachable: true,
175-
denyAdapterContract: true,
175+
operationAdvisoryContract: true,
176176
pathObservableCapability: true,
177177
globalGrokPluginInstalled: true,
178178
globalGrokPluginConfigured: false
@@ -271,14 +271,14 @@ assert.match(entryCheckAssistSuffix({ project: 'x', intent: 'fix' }), /Intent→
271271
assert.match(entryCheckAssistSuffix({ project: 'x', intent: 'fix' }), /fix-default/)
272272
assert.match(entryCheckAssistSuffix({ project: 'x', intent: 'audit' }), /TTFV|workspace-root|scan-hygiene/i)
273273

274-
// Smoke: deny path used by PreToolUse
275-
const deny = adaptHostOutput('grok', 'PreToolUse', {
274+
// Smoke: PreToolUse never carries a DevCodex permission decision.
275+
const operationOutput = adaptHostOutput('grok', 'PreToolUse', {
276276
hookSpecificOutput: {
277277
permissionDecision: 'deny',
278278
permissionDecisionReason: 'workflow-invalid'
279279
}
280280
})
281-
assert.deepStrictEqual(deny, { decision: 'deny', reason: 'workflow-invalid' })
281+
assert.deepStrictEqual(operationOutput, {})
282282

283283
// W8: extra field names for assistant text
284284
const payloadApi = buildLifecyclePayloadUtils({

0 commit comments

Comments
 (0)