Skip to content

[#3254] pin-advance batch: masfeat/OJK additive extension - #224

Merged
saurabhjain1592 merged 4 commits into
mainfrom
feat/3254-batch2-masfeat
Aug 4, 2026
Merged

[#3254] pin-advance batch: masfeat/OJK additive extension#224
saurabhjain1592 merged 4 commits into
mainfrom
feat/3254-batch2-masfeat

Conversation

@saurabhjain1592

@saurabhjain1592 saurabhjain1592 commented Aug 4, 2026

Copy link
Copy Markdown
Member

Summary

Python leg of the #3254 pin-advance batch (operator ruling, comment 5175919799 on getaxonflow/axonflow-enterprise#3254): extends the additive interim to the masfeat/OJK read models, and folds in getaxonflow/axonflow-enterprise#3262 by binding the masfeat dataclasses into the wire-shape contract gate. Additive only - nothing removed, renamed or retyped.

Per-model verdicts (verified against SERVER SOURCE at tag v9.13.0, platform/orchestrator/masfeat/types.go in getaxonflow/axonflow @ df027c788 - NOT inherited from the Go train)

The Python parsers already read the real wire names as fallback spellings (pinned by tests/test_masfeat.py), so the Go train's zero-out verdicts do NOT transfer:

Model Verdict Detail
RegistrySummary fiction: by_use_case/by_status; missing real fields high/medium/low_materiality_count are correct-by-fallback (parser reads wire high_materiality etc., types.go:431-440). by_use_case/by_status have NO wire equivalent (flat counts only) - deprecated with canonical wording. Real fields the SDK lacked - org_id, assessments_due, kill_switches_triggered - added (defaulted, parser reads them).
KillSwitch no fiction triggered_reason is correct-by-fallback (wire trigger_reason, types.go:288). Informational gaps NOT added (not fiction-class): trigger_conditions, restore_reason.
AISystemRegistry fiction: technical_owner Never served anywhere in platform/orchestrator/masfeat/ or masfeat-api.yaml at the tag - always None on read; the register/update write paths send it, unread server-side (kept sending, additive-only). Deprecated with canonical wording pointing at the real owner_email (read into business_owner) + owner_team. business_owner/customer_impact/model_complexity/human_reliance are correct-by-fallback (wire owner_email/risk_rating_impact/risk_rating_complexity/risk_rating_reliance).
OJKAuditExportResponse not modeled Zero OJK references under axonflow/; no OJK client surface in this SDK. Nothing to do.

Deprecation idiom: dataclass docstring "Deprecated:" attribute notes with the canonical wording (dataclasses have no Field descriptions); same choice as the merged audit interim (#223), no runtime warnings.

#3262: masfeat dataclasses bound into the wire-shape gate

Choice (stated per the issue): extract wire names by DRIVING THE REAL PARSERS (*_from_dict) with a key-recording payload - not a pydantic migration (changes the public types: constructor semantics, dataclasses.asdict consumers, isinstance checks - next-major work), and not a hand-declared field-to-wire table (would test the declaration, not the path).

Mechanics: _WireKeyRecorder records every key a parser attempts via get/[] (deliberately NOT in - the kill_switch envelope probe is response-shape dispatch, not a field read; pinned by its own test). Seeds are SOURCE-DERIVED from the v9.13.0 server structs (declared as such, not captures) and carry ONLY real wire names, so every legacy-name read - which always sits first in an x or y chain - is attempted and recorded; a seed containing a legacy name would satisfy the chain early and hide the real-name read (pinned by a seed-hygiene test). The gate diffs consumed keys vs the masfeat-api.yaml schema and holds the result to the same baseline discipline as the pydantic gate. The rename-escape guard, the staleness report and scripts/refresh_wire_shape_baseline.py all learn the dataclass bindings, so the #214 pin-bump regen recomputes this drift instead of silently dropping it.

Red-first evidence (extended gate, PRE-FIX models, current pin 0bd92562)

NEW masfeat dataclass wire-shape drift detected (not covered by baseline):

  AISystemRegistry (dataclass, parser-consumed keys):
    NEW, read by parser but not in OpenAPI: ['owner_email', 'risk_rating_complexity', 'risk_rating_impact', 'risk_rating_reliance']
  KillSwitch (dataclass, parser-consumed keys):
    NEW, read by parser but not in OpenAPI: ['trigger_reason']
  RegistrySummary (dataclass, parser-consumed keys):
    NEW, read by parser but not in OpenAPI: ['high_materiality', 'low_materiality', 'medium_materiality']

FAILED tests/test_wire_shape.py::test_no_new_masfeat_dataclass_vs_spec_drift
1 failed, 16 passed

Note what this shows: the current pin is the FICTION-ERA spec (it declares by_use_case/technical_owner/triggered_reason), so at this pin the gate flags the parsers' REAL-name fallback reads - proof the binding sees the actual wire reads, not the dataclass field names. Post-fix (with the three added RegistrySummary fields) the uncovered set grows by org_id/assessments_due/kill_switches_triggered, as expected. Green after curated baseline entries naming #3254/#3262 and the #214 pin advance: 20/20 at the current pin (post-R3 count). At the v9.13.0 pin these entries flip to the legacy spellings (stated in each note); the #214 regen recomputes them via the extended script.

Batch R3 fixes (commit 946e929)

  • Ghost-read evasion closed. R3 proved data[k] if k in data else None on an absent key left no consumed trace and the gate stayed green at both pins. _WireKeyRecorder now records in probes into a separate probed set and the extractor FAILS on probed-but-never-read keys unless declared in _ENVELOPE_DISPATCH_KEYS (single entry: the kill_switch_from_dict envelope, parser named). Mutation-proven: the ghost read injected into the real registry_summary_from_dict goes red at BOTH pins naming the key (ghost-evasion-red.txt); envelope dispatch stays green; probe-followed-by-read is clean; decoy/negative controls unchanged. Honest residual: reads via iteration/copy (for k in data, dict(data)) are still unrecorded shapes - no bound parser uses them.
  • Stale baseline allowances now hard-fail (test_baseline_has_not_grown_stale), matching the Go SDK's equivalent: a phantom sdk_only allowance is a hole the width of a future regression. Mutation-proven red on an injected phantom_dead_allowance (stale-allowance-red.txt); the committed baselines are regen-exact, so both pins stay green honestly.

Self-tests (machinery, run in the regular suite)

  • Decoy: a parser reading decoy_fiction_field (absent key, get returns None - the exact fiction-read shape) MUST surface as sdk-only drift - caught.
  • Negative control: a parser reading exactly the schema-declared names - zero drift.
  • Presence-probe exclusion and seed-hygiene tests pin the two stated design caveats.

Additive-only public API proof

dataclasses.fields diff, main vs branch, for the three touched models - additions only, all defaulted:

7a8,10
> RegistrySummary.org_id: optional
> RegistrySummary.assessments_due: optional
> RegistrySummary.kill_switches_triggered: optional

Live leg (honest report)

masfeat_get_registry_summary() via the SDK against the session-3254 community v9.13.0 stack (127.0.0.1:38080): HTTP 404 - the masfeat routes are enterprise-gated on a community deployment, as the batch spec anticipated. No capture fabricated; evidence rests on (a) the server-source citations above at tag df027c788 and (b) source-derived fixture tests (declared as source-derived in every test/seed comment).

Test evidence

  • Full suite: 1080 passed, 30 skipped (coverage 82.88 percent, gate 75).
  • tests/test_masfeat.py: new-field population from a source-derived real-shape payload, old-server defaults, deprecated fields stay empty against real-shaped payloads.
  • Wire-shape gate at the current pin: 17/17 with curated entries; red-first output above without them.
  • ruff check/format clean; falsey-clobber lint clean (baseline refresh is line-number shifts only from docstring insertions in masfeat.py, finding set unchanged at 51).

Items not modified in this PR (with justification)

  • KillSwitch.trigger_conditions/restore_reason and AISystemRegistry.data_sources/model_type/version/deployment_date/last_assessment_date/next_assessment_due/updated_by: real wire fields the SDK does not model - informational coverage gaps, not fiction-class wrongness; per the batch canonical this is not a full-coverage exercise. They appear as spec_only drift at the v9.13.0 pin with notes.
  • KillSwitchEvent vs the server's KillSwitchHistory: different schema names, so no name-match binding exists; its parser already dual-reads event_type/action, created_at/performed_at, created_by/performed_by. Out of the four-model batch scope.
  • No pydantic migration of the masfeat models (next-major work, see #3262 choice above).
  • Legacy first-choice spellings (high_materiality_count, triggered_reason reads, etc.) kept - additive-only interim; renames ride the next major.
  • OJK surface: not modeled in this SDK, nothing added (adding a whole new OJK client surface is outside "fix fiction" scope).

Merge order: this PR first, then #214 (the v9.13.0 spec pin re-land, stacked on this branch).

Refs getaxonflow/axonflow-enterprise#3254, getaxonflow/axonflow-enterprise#3262.

…iction deprecations, dataclass gate binding (#3262)

Per the operator ruling on getaxonflow/axonflow-enterprise#3254
(pin-advance batch), verified per model against server source at tag
v9.13.0 (platform/orchestrator/masfeat/types.go) rather than inheriting
the Go train's verdicts - the Python parsers already read the real wire
names as fallbacks, so the zero-out class does not apply here:

- RegistrySummary: ADD real fields org_id/assessments_due/
  kill_switches_triggered (parser reads them); DEPRECATE by_use_case/
  by_status (never served on 9.x, no wire equivalent). The *_count
  fields are correct-by-fallback (wire high/medium/low_materiality).
- KillSwitch: no fiction (triggered_reason is populated from the real
  trigger_reason); trigger_conditions/restore_reason stated as
  informational gaps, not added.
- AISystemRegistry: DEPRECATE technical_owner (never served; the wire
  carries owner_email/owner_team); business_owner/customer_impact/
  model_complexity/human_reliance are correct-by-fallback.
- OJKAuditExportResponse: not modeled in this SDK; nothing to do.

#3262: the masfeat dataclasses are now BOUND into the wire-shape gate
by driving the real *_from_dict parsers with a key-recording payload
(source-derived seeds carrying only real wire names so every legacy
fallback read is attempted and recorded). Chosen over a pydantic
migration (public-type change, next-major work) and over a declared
mapping table (would test the declaration, not the path). The rename-
escape guard, staleness report and the baseline regenerator all learn
the dataclass bindings. Red-first proven at the current pin; curated
baseline entries name #3254 and the PR #214 pin advance. Self-tests:
decoy fiction read caught, negative control, presence-probe exclusion,
seed hygiene.

Live leg: masfeat routes are enterprise-gated on the community stack
(404 observed via the SDK's own masfeat_get_registry_summary) -
evidence rests on server-source citations and source-derived fixtures,
declared as such.

falsey_clobber baseline: line-number refresh only (docstring insertions
in masfeat.py shifted 11 entries; finding set unchanged at 51).

Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com>
… (#3254)

Drives the real SDK's masfeat_get_registry_summary() against a real
agent. On Enterprise it asserts the typed parse of the #3254 additions
and that the deprecated fiction fields stay empty; on Community the
masfeat surface is Enterprise-gated - the 404 is DIAGNOSED (the stack
must still prove reachable via /health through the same client) and
reported as GATED, not skipped and not counted as covering the
Enterprise assertions. Observed on the live community v9.13.0 stack:
GATED with /health healthy.

Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com>
python scripts/refresh_wire_shape_baseline.py puts scripts/ (not the
repo root) at sys.path[0], so 'import axonflow' inside the loaded test
helpers resolved through the venv's editable install - which can point
at a DIFFERENT checkout. Observed during the #3254 batch-2 regen: a
sibling checkout's pre-fix masfeat parser produced a wrong-but-plausible
RegistrySummary drift entry (org_id/assessments_due/
kill_switches_triggered recorded spec_only) with no error. The script
now inserts its own repo root at sys.path[0] before importing.

Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com>
saurabhjain1592 added a commit that referenced this pull request Aug 4, 2026
Refreshes tests/fixtures/wire_shape_baseline.json via
scripts/refresh_wire_shape_baseline.py against a checkout of
getaxonflow/axonflow tag v9.13.0 (df027c788) docs/api. Stacked on the
#3254 pin-advance batch (PR #224): the audit fiction fields,
request_type, and now the masfeat legacy spellings + fiction fields
(by_use_case/by_status/technical_owner/triggered_reason/*_count) stay
as consciously noted deprecated-pending-removal drift entries naming
#3254 and the coordinated next-major burn-down. The masfeat dataclass
entries are computed by the #3262-extended regenerator from the real
parsers' consumed wire keys.

Regen result: 15 per-model drift entries (12 from the July recipe + 3
masfeat dataclass bindings), MCPCheckInputRequest out (spec declares
content_type + tool), 93 registered models (90 pydantic-era + 3
dataclass), spec-bug-pending count 0.

Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com>
…line allowances now hard-fail

Batch R3 on #224, two latent findings fixed rather than filed:

1. The ghost-read evasion was concretely exploitable: a parser doing
   'data[k] if k in data else None' on a key absent from the seed left
   no consumed trace, and the gate stayed green at both pins.
   _WireKeyRecorder now records __contains__ into a separate probed
   set, and the extractor FAILS on probed-but-never-read keys unless
   declared in _ENVELOPE_DISPATCH_KEYS (response-shape unwrapping, the
   kill_switch_from_dict envelope - the only entry, parser named).
   Proven by mutation: the ghost read injected into the real
   registry_summary_from_dict goes red at BOTH pins with the key named;
   the envelope dispatch stays green; the decoy/negative controls still
   pass; a probe followed by a read is clean.

2. test_baseline_has_not_grown_stale was print-only, so a phantom
   sdk_only allowance passed silently - a hole the width of a future
   regression, and a cross-SDK asymmetry (Go's equivalent hard-fails).
   It now fails listing the dead allowances. Proven by mutation: a
   phantom entry appended to KillSwitch.sdk_only goes red naming it;
   the committed baselines are regen-exact so both pins stay green
   honestly.

Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com>
saurabhjain1592 added a commit that referenced this pull request Aug 4, 2026
Refreshes tests/fixtures/wire_shape_baseline.json via
scripts/refresh_wire_shape_baseline.py against a checkout of
getaxonflow/axonflow tag v9.13.0 (df027c788) docs/api. Stacked on the
#3254 pin-advance batch (PR #224): the audit fiction fields,
request_type, and now the masfeat legacy spellings + fiction fields
(by_use_case/by_status/technical_owner/triggered_reason/*_count) stay
as consciously noted deprecated-pending-removal drift entries naming
#3254 and the coordinated next-major burn-down. The masfeat dataclass
entries are computed by the #3262-extended regenerator from the real
parsers' consumed wire keys.

Regen result: 15 per-model drift entries (12 from the July recipe + 3
masfeat dataclass bindings), MCPCheckInputRequest out (spec declares
content_type + tool), 93 registered models (90 pydantic-era + 3
dataclass), spec-bug-pending count 0.

Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com>
@saurabhjain1592
saurabhjain1592 merged commit ea22029 into main Aug 4, 2026
18 checks passed
@saurabhjain1592
saurabhjain1592 deleted the feat/3254-batch2-masfeat branch August 4, 2026 09:45
saurabhjain1592 added a commit that referenced this pull request Aug 4, 2026
Refreshes tests/fixtures/wire_shape_baseline.json via
scripts/refresh_wire_shape_baseline.py against a checkout of
getaxonflow/axonflow tag v9.13.0 (df027c788) docs/api. Stacked on the
#3254 pin-advance batch (PR #224): the audit fiction fields,
request_type, and now the masfeat legacy spellings + fiction fields
(by_use_case/by_status/technical_owner/triggered_reason/*_count) stay
as consciously noted deprecated-pending-removal drift entries naming
#3254 and the coordinated next-major burn-down. The masfeat dataclass
entries are computed by the #3262-extended regenerator from the real
parsers' consumed wire keys.

Regen result: 15 per-model drift entries (12 from the July recipe + 3
masfeat dataclass bindings), MCPCheckInputRequest out (spec declares
content_type + tool), 93 registered models (90 pydantic-era + 3
dataclass), spec-bug-pending count 0.

Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com>
saurabhjain1592 added a commit that referenced this pull request Aug 4, 2026
Refreshes tests/fixtures/wire_shape_baseline.json via
scripts/refresh_wire_shape_baseline.py against a checkout of
getaxonflow/axonflow tag v9.13.0 (df027c788) docs/api. Stacked on the
#3254 pin-advance batch (PR #224): the audit fiction fields,
request_type, and now the masfeat legacy spellings + fiction fields
(by_use_case/by_status/technical_owner/triggered_reason/*_count) stay
as consciously noted deprecated-pending-removal drift entries naming
#3254 and the coordinated next-major burn-down. The masfeat dataclass
entries are computed by the #3262-extended regenerator from the real
parsers' consumed wire keys.

Regen result: 15 per-model drift entries (12 from the July recipe + 3
masfeat dataclass bindings), MCPCheckInputRequest out (spec declares
content_type + tool), 93 registered models (90 pydantic-era + 3
dataclass), spec-bug-pending count 0.

Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant