Commit c88c871
authored
* fix(wire-shape): close 4 review gaps from #182 + #184 follow-up
CI-internal hardenings; no user-visible behaviour change.
- refresh.js: `--sha` with no value used to be silently consumed as
the positional `specsDir`, masking the missing argument and routing
the script through the gitHeadSHA fallback. Now exits 2 with a
clear message.
- validate.js: an empty `registered_types` paired with non-empty
cross_spec / intra_file / per_type_drift entries used to silently
disable Gate 4 (rename-escape). The validator now treats this
combination as a baseline corruption and fails the run. A genuinely
fresh baseline (every section empty) still skips the gate as
before, since the first-pin run has nothing to guard.
- lib.js writeBaseline: a crash between writeFileSync(tmp, ...) and
renameSync(tmp, BASELINE_PATH) used to leave a `.tmp.<pid>`
sidecar, which a future writer with the recycled PID would
collide on. Now wraps in try/catch + unlinkSync on any throw.
- lib.js extractInterfaceProps: an `interface X extends Y` would
silently under-report fields because we walk only iface.members.
No such interface ships in the SDK today; the gate now warns
loudly the first time one appears so coverage gets resolved
before drift can hide. Full heritage walking is the proper fix
if this becomes a routine pattern.
Verifications:
- refresh.js with `--sha` as last token exits 2; with `--sha foo` works.
- validate.js with corrupted baseline (empty registered_types but
populated drift/cross_spec) exits 1 with explanatory message.
- writeBaseline temp-file is cleaned up on synthetic write error.
- Real baseline regenerates byte-identical to origin/main.
* fix(wire-shape): close stale cross-spec baseline shielding gap
Gate 1 (cross-spec divergence) only iterated currently observed
schemas. A baselined divergence that the platform has since
reconciled silently lingered in the baseline forever; the same old
incompatible shape could be reintroduced and pass the gate because
its fingerprint matched a stale entry that should have been deleted.
Adds the reverse pass that Gate 2 already does for intra-file
duplicates: any baselined cross-spec name that is no longer
observed in the current specs fails the run with a pointer at the
specific baseline key to delete.
Mirrors the same fix landing on the Java arm in PR #137.
Verified locally:
- Positive run on clean baseline still exits 0.
- Adding a phantom 'PhantomDivergence' entry to baseline
cross_spec_duplicates causes the validator to exit 1 with a clear
remove-from-baseline message naming the stale key.
* chore: remove stray session-export artifact from previous commit
The accidental git add -A in the prior commit swept up a
session-export .txt file that lives in this worktree. Removing.
* fix(wire-shape): canonicalize TS interface fields to wire snake_case
The TS validator was extracting in-memory property names (camelCase
by TS idiom) and diffing them directly against OpenAPI snake_case
wire names, generating ~22 false-positive drift entries (Category A
in the audit) where the SDK's hand-rolled transformer in
client.ts already correctly bridged the two names. The other three
SDK validators don't have this issue: pydantic alias for Python,
`json:"…"` struct tag for Go, `@JsonProperty(…)` for Java — each
extracts the WIRE name natively. TS has no first-class equivalent.
This change adds equivalent wire-name extraction for TS:
- toSnakeCase() converts camelCase to snake_case with three boundaries
(acronym→word, lowercase→uppercase, lowercase→digit). The digit
boundary fixes ModelPricing.inputPer1k → input_per_1k, which the
earlier categorization regex got wrong.
- loadTransformerEvidence() reads src/client.ts once and collects
every snake_case-shaped identifier. This is the bridge audit: a
TS field is treated as 'bridged to its snake_case wire form' only
when the snake form actually appears somewhere in client.ts.
- canonicalizeWireNames() applies the conversion per-field. If the
snake form is found in client.ts, the field is normalized; if not,
the camelCase form is kept and the type is recorded as having
unbridged fields.
- The safety net: a type's unbridged fields are warned about ONLY
when the same type has at least one OTHER field that IS bridged.
If no field in the type is bridged, the type is almost certainly
SDK-internal (configs, options, adapter metadata, etc.) and the
whole type isn't wire-bound; warning would be noise.
Effect: 22 Category A drift entries disappear from the baseline
(AuditToolCallRequest, BudgetCheckRequest, FEATAssessment,
KillSwitch, ModelPricing, PlanVersionEntry, PolicyMatch, etc.).
63 → 41 drift entries. The 19 unbridged-field warnings the
validator now emits all line up with audit-flagged broken/orphan
reads in client.ts (PolicyEvaluationResult.databaseAccessed,
StaticPolicy.hasOverride, StepGateResponse.policiesMatched, etc.) —
the validator is now correctly distinguishing 'bridged drift' from
'genuinely missing transformer'.
No SDK property names changed. Wire behavior is unchanged. This is
purely a validator measurement fix; the SDK's existing transformer
architecture is preserved as-is.
Per platform-side spec corrections needed (filed separately):
- AISystemRegistry.materiality_classification (#1708)
- DynamicPolicyInfo schema completely wrong (#1709)
* feat(types): wire-shape alignment sweep (Cat B + RENAME_SAFE + UNRECOVERABLE)
Audit-driven cleanup against the wire-shape contract gate. All
changes are additive (new fields are optional). Where a TS field
was reading `undefined` because the decoder is JSON.parse passthrough
and the wire emits a different key, the wire-canonical name is added
and the orphan field is marked `@deprecated` (kept for compile-time
compat; removed in v7).
Per-type changes (full detail in CHANGELOG):
Pure additions (Cat B):
- WebhookSubscription.{secret, org_id, tenant_id} — security-critical;
`secret` is the HMAC-SHA256 signing key required to verify inbound
webhook payload signatures.
- StepGateRequest.{cost_usd, tokens_in, tokens_out}
- StepGateResponse.decision_id
- ListWorkflowsResponse.{limit, offset}
- StaticPolicy.{policy_id, priority, has_override}
- CreateStaticPolicyRequest.{priority, tags}
- UpdateStaticPolicyRequest.{priority, tags}
- UpdatePlanRequest.metadata
- UsageBreakdownItem.group_by
- BudgetAlert.acknowledged
- Budget.{org_id, tenant_id}
- WorkflowStatusResponse.metadata
- ExecutionSnapshot.retryCount
- Finding.article
RENAME_SAFE (orphan reads → wire-aligned canonical, old marked @deprecated):
- StepGateResponse.policies_evaluated/policies_matched (snake) +
policiesEvaluated/policiesMatched @deprecated
- StaticPolicy.has_override (canonical) + hasOverride @deprecated
- DynamicPolicyMatch.message + reason @deprecated
- ExfiltrationCheckInfo.{exceeded, limit_type} + within_limits @deprecated
- PolicyOverride.{id, enabled_override} + active @deprecated
- PolicyVersion.{id, policy_id, change_summary, snapshot} +
{changeDescription, previousValues, newValues} @deprecated
- CancelPlanResponse.success + message @deprecated
- CreateWorkflowResponse.started_at + {created_at, source} @deprecated
- UsageRecord.{created_at, success, error_message, latency_ms,
team_id, tenant_id, user_id, workflow_id} + timestamp @deprecated
- ResumePlanResponse.result + 5 declared-but-never-populated fields
marked @deprecated (workflowId, message, stepResult, nextStep,
nextStepName, totalSteps)
- PolicyEvaluationResult.{required_actions, processing_time_ms,
database_accessed} (snake) + camelCase forms @deprecated
UNRECOVERABLE realignments (no decoder; safe to add wire shape):
- EffectivePoliciesResponse: tier-stratified wire shape
({static, dynamic, tenant_id, organization_id, computed_at})
- Policy: rich wire shape (policy_id, category, tier, pattern,
severity, action, actions, conditions, description,
organization_id, tenant_id, created_at/_by, updated_at/_by,
version) — was exposing only 5 of 21 wire fields
- PlanResponse: wire top-level fields (success, version, result,
error, workflow_execution_id, policy_info)
Transformer updates:
- BudgetAlert decoder propagates acknowledged
- UsageBreakdownItem decoder propagates groupBy
- ExecutionSnapshot decoder propagates retryCount
Wire-shape baseline regenerated:
- 41 → 34 drift entries
- All Cat B types fully resolved (RESOLVED ✓)
- Remaining entries are all SDK-only fields (mostly @deprecated
aliases retained for type-compat + Plugin Batch 1 SDK additions
pending platform-side spec coverage)
Filed alongside (no SDK change needed; spec is wrong):
- axonflow-enterprise#1708 — AISystemRegistry.materiality_classification
- axonflow-enterprise#1709 — DynamicPolicyInfo schema wrong shape
Tests: 859 pass. Lint clean.
* fix(client): propagate v6 fields through transformers (read + write)
Companion to the type-definition sweep in this PR. The type updates
added wire-canonical fields, but the hand-rolled transformers in
client.ts weren't updated in parallel — so the fields were unreachable
at runtime even though TypeScript types said they existed.
The 5 reported gaps + 4 systematic-audit-found gaps = 9 fixes:
Read-path fixes (decoders that dropped new wire fields):
- generatePlan: PlanResponse now carries the wire top-level fields
(success, version, result, error, workflow_execution_id,
policy_info), sourced from agentResponse.* with data.* fallback.
- resumePlan: ResumePlanResponse.result now populated from
data.result. The deprecated `message` alias remains populated for
back-compat.
- getStaticPolicyVersions: inline-typed wire shape now declares
the canonical fields (id, policy_id, change_summary, snapshot)
alongside the legacy `change_description`/`previous_values`/
`new_values` keys; the transformer emits both halves.
- listUsageRecords: UsageRecord transformer now emits the 8 new
canonical fields (created_at, success, error_message, latency_ms,
team_id, tenant_id, user_id, workflow_id). Legacy `timestamp`
still emitted (it has always been undefined; both kept for source
compat).
- getExecution: inline step shape now declares retry_count, and
the per-step mapper emits retryCount. (getExecutionSteps was
already correct; only this method was out of sync.)
- mapBudgetResponse: emits tenant_id, org_id (used by every Budget-
returning method).
- mapFindingResponse: emits article (used by mapAssessmentResponse).
Write-path fixes (request builders that dropped new SDK fields):
- updatePlan: body now includes metadata when set on the request.
- createStaticPolicy: requestBody now includes priority, tags when
set on the request.
Verified clean (no gap): updateStaticPolicy (passthrough; field
names match wire), stepGate (passthrough; new tokens_in/tokens_out/
cost_usd field names match wire), every method that flows through
orchestratorRequest<T>/policyRequest<T> with a typed cast (the JSON
parses naturally into the typed shape, so new fields appear without
explicit transformer code).
Meta-pattern observed: any method with a hand-rolled mapper helper
or an explicit return-literal builder is a candidate gap when types
gain fields. Methods that pass through to orchestratorRequest<T>
inherit the new fields for free. The audit covered every type
touched in the sweep across both directions.
Tests: 859 pass. Lint clean.
* fix(client): cancelPlan now propagates the canonical success field
Same class as the 5 transformer gaps fixed in commit 2fac16f. The
type-update added CancelPlanResponse.success but the cancelPlan()
method's return literal didn't include it. The deprecated `message`
slot is also kept populated so existing callers that read it see
no behavior change (it has always been undefined against current
servers, but zero-clobber).
The systematic audit clearly missed cancelPlan in the previous
round. The expanded sweep below documents every type/method pair
to keep the contract honest:
Read-path verifications (every method that returns a touched type):
- BudgetAlert via getBudgetAlerts: acknowledged emitted ✓ (round 2)
- UsageBreakdownItem via getUsageBreakdown: groupBy emitted ✓ (round 2)
- ExecutionSnapshot via getExecutionSteps + getExecution:
retryCount emitted ✓ (round 2)
- PolicyEvaluationResult: embedded in PlanResponse.policy_info
populated by generatePlan ✓ (round 2)
- PlanResponse via generatePlan: 6 wire top-level fields ✓ (round 2)
- CancelPlanResponse via cancelPlan: success now emitted (this commit)
- ResumePlanResponse via resumePlan: result emitted ✓ (round 2)
- StaticPolicy via list/get/create/update/toggle/getEffective:
passthrough through policyRequest<T> ✓
- PolicyOverride via createPolicyOverride/listPolicyOverrides:
passthrough through policyRequest<T> ✓
- PolicyVersion via getStaticPolicyVersions: id, policy_id,
change_summary, snapshot emitted ✓ (round 2)
- WorkflowStatusResponse via getWorkflow: passthrough ✓
- ListWorkflowsResponse via listWorkflows: passthrough ✓
- WebhookSubscription via createWebhook/getWebhook/updateWebhook:
passthrough ✓
- ExfiltrationCheckInfo, DynamicPolicyMatch: embedded in MCP
response shapes; passthrough ✓
- Finding via mapFindingResponse: article emitted ✓ (round 2)
- Budget via mapBudgetResponse (used by createBudget/getBudget/
listBudgets/updateBudget/checkBudget/getBudgetStatus):
tenant_id + org_id emitted ✓ (round 2)
- UsageRecord via listUsageRecords: 8 wire fields emitted ✓ (round 2)
- StepGateResponse via stepGate: passthrough; decision_id +
policies_evaluated + policies_matched flow through ✓
- CreateWorkflowResponse via createWorkflow: passthrough; started_at
flows through ✓
Write-path verifications (every method that accepts a touched
request type):
- StepGateRequest via stepGate: JSON.stringify passthrough; new
cost_usd / tokens_in / tokens_out names match wire ✓
- UpdatePlanRequest via updatePlan: metadata in body builder ✓
(round 2)
- CreateStaticPolicyRequest via createStaticPolicy: priority + tags
in body builder ✓ (round 2)
- UpdateStaticPolicyRequest via updateStaticPolicy: passthrough;
field names match wire ✓
Falsey-clobber audit: 0 || patterns introduced in this PR's
client.ts diff (verified with git diff grep). The transformer code
uses ?? semantics or explicit guards.
Tests: 859 pass. Lint clean.
* chore(transformer-coverage): refresh baseline after v6 sweep burns down resumePlan
PR #185's transformer fix populated all 5 fields previously missing on
ResumePlanResponse (nextStep, nextStepName, stepResult, totalSteps,
workflowId). Drop the stale baseline entry; remaining entries are
executePlan and getPlanStatus on PolicyEvaluationResult, untouched
this round.
1 parent 27d6fa5 commit c88c871
15 files changed
Lines changed: 715 additions & 844 deletions
File tree
- .lint_baselines
- scripts/wire-shape
- src
- types
- tests/fixtures
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
6 | | - | |
| 5 | + | |
7 | 6 | | |
8 | 7 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
10 | 38 | | |
11 | 39 | | |
12 | 40 | | |
13 | 41 | | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
14 | 48 | | |
15 | 49 | | |
16 | 50 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
| 30 | + | |
30 | 31 | | |
31 | 32 | | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
32 | 72 | | |
33 | 73 | | |
34 | 74 | | |
| |||
174 | 214 | | |
175 | 215 | | |
176 | 216 | | |
177 | | - | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
178 | 295 | | |
179 | 296 | | |
180 | 297 | | |
| |||
220 | 337 | | |
221 | 338 | | |
222 | 339 | | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
223 | 365 | | |
224 | 366 | | |
225 | 367 | | |
| |||
290 | 432 | | |
291 | 433 | | |
292 | 434 | | |
293 | | - | |
294 | | - | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
295 | 448 | | |
296 | 449 | | |
297 | 450 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
44 | 44 | | |
45 | 45 | | |
46 | 46 | | |
47 | | - | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
48 | 55 | | |
49 | 56 | | |
50 | 57 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
104 | 104 | | |
105 | 105 | | |
106 | 106 | | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
107 | 120 | | |
108 | 121 | | |
109 | 122 | | |
| |||
223 | 236 | | |
224 | 237 | | |
225 | 238 | | |
226 | | - | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
227 | 262 | | |
228 | 263 | | |
229 | 264 | | |
| |||
0 commit comments