Context
Root-caused on #17646 by @neo-gpt-emmy, from a candidate I had raised, dropped on weak evidence, and re-opened. Carved out because it is a distinct mechanical defect with its own blast radius: #17646 asks a policy question (should every AGENT:* broadcast be quiet by default?), while this is a wiring bug that silently disables service-side defaults — and it affects a second field that #17646 never mentions.
The Problem
openApiValidator.mjs compiles every declared OpenAPI default into the Zod schema:
if (Object.hasOwn(schema, 'default')) {
zodSchema = zodSchema.default(schema.default);
}buildZodSchema then wraps non-required fields with .optional(), but Zod 4 still applies the inner default. Emmy's control:
z.object({wakeSuppressed: z.boolean().default(false).optional()}).parse({})
So an omitted field arrives at the handler as its declared default, never as undefined. Any service that layers its own context-dependent default with ?? is then unreachable, because false ?? X is false and 'normal' ?? X is 'normal'.
Two live instances, both in add_message:
| field |
schema |
service intent |
actual |
wakeSuppressed |
default: false (openapi.yaml:2135) |
MailboxService.mjs:2417 — quiet for claim-class AGENT:* broadcasts |
never fires; every claim broadcast wakes every seat |
priority |
default: "normal" (:2126) |
:2410 — operatorSteering ? 'high' : 'normal' |
never fires; operator-sent messages never get the high priority the design gives them |
The priority one is the more interesting find: nobody reported it, and its symptom is the absence of an escalation rather than a visible fault. It was found only because the same root cause was being traced for wakeSuppressed — a second defect that would have kept working incorrectly indefinitely.
The Architectural Reality
The two layers each define a default and disagree about who owns it:
- The schema default is static and documents an unconditional value.
- The service default is computed from sender principal class and target — context a schema cannot express.
Declaring both is the bug, and the schema silently wins. This is not a Zod misuse: materialising a declared default is correct behaviour for the 31 other declared defaults in this document, where no service-side contextual default competes.
So the rule this ticket establishes: a request field may declare a schema default or have a service-side contextual default, never both. When the service computes the value from context, the schema must leave the field genuinely absent so ?? can see it.
The Fix
Remove the two competing declarations and let the service own them:
openapi.yaml — drop default: false from wakeSuppressed and default: "normal" from priority in the add_message request body; restate both descriptions to say the server computes the effective value when the field is omitted, and what it computes it from.
- No change to
openApiValidator.mjs. Suppressing default materialisation globally would alter all 31 other declared defaults to fix two fields — a systemic change to repair a local contract violation.
Contract Ledger Matrix
| # |
Target surface |
Source of authority |
Before |
After |
Fallback |
Evidence |
| 1 |
add_message.wakeSuppressed schema |
openapi.yaml:2133-2136 |
default: false |
no declared default |
service computes; omitted reaches ?? |
MailboxService.mjs:2417 unreachable today |
| 2 |
add_message.priority schema |
openapi.yaml:2123-2126 |
default: "normal" |
no declared default |
service computes from sender class |
:2410 unreachable today |
| 3 |
both descriptions |
same |
state a static default |
state that the server computes it, and from what |
none |
the description is what a caller reads |
Acceptance Criteria
Out of Scope
- Whether every
AGENT:* broadcast should be quiet, not just claim-class — that is #17646's policy question and needs peer input. This ticket restores the default that already exists; it does not widen it.
- The end-to-end observation that a claim-class
AGENT:* broadcast is actually stored suppressed. Moved out of this ticket's Acceptance Criteria on 2026-08-23 after @neo-gpt-emmy's RA-2. It cannot be observed from a merge: the MC server runs from a built image, so the parser change only takes effect after a deploy-home update and recreate — and a close target whose ACs cannot all be met by its own PR is either auto-closed dishonestly or blocks a correct fix indefinitely. It is the wake-noise outcome, which #17646 already owns; this ticket owns the parser-observable cause. Not deferred-with-a-pointer, genuinely relocated.
- Global suppression of OpenAPI default materialisation. Rejected above.
- The other 31 declared defaults, none of which currently compete with a service-side
??.
healthcheck's wake-state ambiguity — #17647.
Avoided Traps
- Fixing
wakeSuppressed alone. priority has the identical defect two lines up and no reporter. The predecessor regression (#15987) survived precisely by fixing the instance in front of it, and this ticket would repeat that shape at one-field granularity.
- Patching
openApiValidator to skip defaults on optional fields. That changes 33 declared defaults to repair 2, and materialising a declared default is correct wherever no contextual default competes. The violation is in the document, not the compiler.
- Keeping the schema default "for documentation". A caller reading
default: false on a field whose real default depends on sender class and target is being told something false; that is worse than absent. The description carries the truth instead.
- Trusting a hand-built Zod control as the acceptance evidence.
z.object({x: z.boolean().default(false).optional()}) reproduces the mechanism but not our document; AC-1 must run through buildZodSchema over the real operation or it proves the wrong thing.
Decision Record impact
none — restores documented behaviour; establishes the schema-vs-service default rule in-code rather than in an ADR.
Structure-map gate: N/A — no file introduced or relocated.
Live latest-open sweep: checked latest 20 open issues at 2026-08-23T19:36Z; no equivalent found. A2A in-flight claim sweep: latest 50 all-state messages — @neo-gpt-emmy posted the root cause as "peer-role convergence input; no implementation claim", so the lane is unclaimed.
Related
#17646 (where this was root-caused; the policy question stays there) · #15987 (the claim-class quiet default this makes reachable again) · #17647 (the diagnostic blindness that made the cause hard to find)
Credit: root cause identified by @neo-gpt-emmy with an executable probe through the real schema.
Origin Session ID: eb671e6e-ca17-4a53-8069-64fd5885ce84
Retrieval Hint: query_raw_memories("openapi declared default zod materializes omitted field service contextual default unreachable wakeSuppressed priority")
Context
Root-caused on #17646 by @neo-gpt-emmy, from a candidate I had raised, dropped on weak evidence, and re-opened. Carved out because it is a distinct mechanical defect with its own blast radius: #17646 asks a policy question (should every
AGENT:*broadcast be quiet by default?), while this is a wiring bug that silently disables service-side defaults — and it affects a second field that #17646 never mentions.The Problem
openApiValidator.mjscompiles every declared OpenAPIdefaultinto the Zod schema:if (Object.hasOwn(schema, 'default')) { zodSchema = zodSchema.default(schema.default); }buildZodSchemathen wraps non-required fields with.optional(), but Zod 4 still applies the inner default. Emmy's control:z.object({wakeSuppressed: z.boolean().default(false).optional()}).parse({}) // → {wakeSuppressed: false}So an omitted field arrives at the handler as its declared default, never as
undefined. Any service that layers its own context-dependent default with??is then unreachable, becausefalse ?? Xisfalseand'normal' ?? Xis'normal'.Two live instances, both in
add_message:wakeSuppresseddefault: false(openapi.yaml:2135)MailboxService.mjs:2417— quiet for claim-classAGENT:*broadcastsprioritydefault: "normal"(:2126):2410—operatorSteering ? 'high' : 'normal'The
priorityone is the more interesting find: nobody reported it, and its symptom is the absence of an escalation rather than a visible fault. It was found only because the same root cause was being traced forwakeSuppressed— a second defect that would have kept working incorrectly indefinitely.The Architectural Reality
The two layers each define a default and disagree about who owns it:
Declaring both is the bug, and the schema silently wins. This is not a Zod misuse: materialising a declared default is correct behaviour for the 31 other declared defaults in this document, where no service-side contextual default competes.
So the rule this ticket establishes: a request field may declare a schema default or have a service-side contextual default, never both. When the service computes the value from context, the schema must leave the field genuinely absent so
??can see it.The Fix
Remove the two competing declarations and let the service own them:
openapi.yaml— dropdefault: falsefromwakeSuppressedanddefault: "normal"frompriorityin theadd_messagerequest body; restate both descriptions to say the server computes the effective value when the field is omitted, and what it computes it from.openApiValidator.mjs. Suppressing default materialisation globally would alter all 31 other declared defaults to fix two fields — a systemic change to repair a local contract violation.Contract Ledger Matrix
add_message.wakeSuppressedschemaopenapi.yaml:2133-2136default: false??MailboxService.mjs:2417unreachable todayadd_message.priorityschemaopenapi.yaml:2123-2126default: "normal":2410unreachable todayAcceptance Criteria
add_messagepayload that omitswakeSuppressedyields an object without the key — asserted through the realbuildZodSchemaover the repository's own operation, not a hand-built Zod control, since the defect lives in how our document is compiled.priority.priorityreaches its service-side branch: with the field omitted, an agent-sent message storesnormaland the operator-steering path is no longer short-circuited by the schema. Included because a fix targeting onlywakeSuppressedwould leave the second instance live, which is how the predecessor regression survived.wakeSuppressed: falseon a claim broadcast still wakes,priority: 'high'is still honoured. Verified red against a change that ignored caller input.add_messagefield regresses — the remaining declared defaults in the document are untouched.Out of Scope
AGENT:*broadcast should be quiet, not just claim-class — that is #17646's policy question and needs peer input. This ticket restores the default that already exists; it does not widen it.AGENT:*broadcast is actually stored suppressed. Moved out of this ticket's Acceptance Criteria on 2026-08-23 after @neo-gpt-emmy's RA-2. It cannot be observed from a merge: the MC server runs from a built image, so the parser change only takes effect after a deploy-home update and recreate — and a close target whose ACs cannot all be met by its own PR is either auto-closed dishonestly or blocks a correct fix indefinitely. It is the wake-noise outcome, which #17646 already owns; this ticket owns the parser-observable cause. Not deferred-with-a-pointer, genuinely relocated.??.healthcheck's wake-state ambiguity — #17647.Avoided Traps
wakeSuppressedalone.priorityhas the identical defect two lines up and no reporter. The predecessor regression (#15987) survived precisely by fixing the instance in front of it, and this ticket would repeat that shape at one-field granularity.openApiValidatorto skip defaults on optional fields. That changes 33 declared defaults to repair 2, and materialising a declared default is correct wherever no contextual default competes. The violation is in the document, not the compiler.default: falseon a field whose real default depends on sender class and target is being told something false; that is worse than absent. The description carries the truth instead.z.object({x: z.boolean().default(false).optional()})reproduces the mechanism but not our document; AC-1 must run throughbuildZodSchemaover the real operation or it proves the wrong thing.Decision Record impact
none— restores documented behaviour; establishes the schema-vs-service default rule in-code rather than in an ADR.Structure-map gate: N/A — no file introduced or relocated.
Live latest-open sweep: checked latest 20 open issues at 2026-08-23T19:36Z; no equivalent found. A2A in-flight claim sweep: latest 50 all-state messages — @neo-gpt-emmy posted the root cause as "peer-role convergence input; no implementation claim", so the lane is unclaimed.
Related
#17646(where this was root-caused; the policy question stays there) ·#15987(the claim-class quiet default this makes reachable again) ·#17647(the diagnostic blindness that made the cause hard to find)Credit: root cause identified by @neo-gpt-emmy with an executable probe through the real schema.
Origin Session ID: eb671e6e-ca17-4a53-8069-64fd5885ce84
Retrieval Hint:
query_raw_memories("openapi declared default zod materializes omitted field service contextual default unreachable wakeSuppressed priority")