Commit 4dd76a7
chore(allowlist): fold cleanup + spec-driven REST + §1 header (java) (#63)
* chore(allowlist): wave-2 fold-branch reconciliation (crud_bases + B1 + composition + §1)
Reconcile the Java surface against porting-sdk@fix/agentbase-mixin-flatten-fold
(the fold branch carrying the AgentBase-mixin, B1 composition-attr, typed SWML-verb,
and spec-driven REST crud_bases folds).
Emission (scripts/enumerate_surface.py):
- crud_bases map: emit a top-level `crud_bases` map (25 resources) keyed by the
reference `<module>.<Class>` path, read from the generated rest_signatures.json
sidecar (base + typed bind). diff_port_surface._fold_crud_methods now folds each
resource's inherited CRUD ops structurally via the ref∪port map — no per-resource
allow-list, no per-op rename.
- composition-delegate strip (§4c.1): drop the flattened pass-through copies of
render_swml/get_contexts/get_raw_prompt/create_tool_token/extract_sip_username
from AgentBase (guarded on the method already being emitted on its canonical
helper class — SwmlRenderer/PromptManager/SessionManager/SWMLService).
- B1 composition-attr getter folds (rename-not-omission): strip the get_ prefix so
Java getters fold onto the reference attribute name —
getSkillManager→skill_manager (global), and scoped per-(module,class):
PromptObjectModel.getSections→sections, Section.getSubsections→subsections,
Action/Message.getResult→result, WebService.getSecurity→security.
Allow-lists (dual-keyed: folded `agentbase-family.*` for SURFACE-DIFF + unfolded
`agent_base.AgentBase.*` twins kept for the DRIFT/SIGNATURE gate):
- PORT_ADDITIONS: 25 genuine port-only AgentBase-family extensions re-expressed with
crisp per-method WHY (builder/clone/MCP/SIP-routing/getter/query idioms; no ref twin).
- PORT_OMISSIONS: 5 folded family omission keys; 7 B1 composition-attr omissions
(logger×4 module-level-no-instance-field, AgentServer.agents, SWMLService.security,
SwaigRequest.argument — genuine non-name absence per rename-not-omission audit);
173 generated-model field-accessor omissions (DEFERRED generator-parity item —
FLAGGED FOR OWNER SIGN-OFF; go/rust/cpp/ts/php emit typed field members, java/ruby/
dotnet emit method-less DTOs).
- §1 allow-list-discipline header added to all four Tier-A files.
Coordinated-With: porting-sdk@fix/agentbase-mixin-flatten-fold
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NqhUoqrbptHNS3cypq9s6t
* chore(allowlist): sweep dead entries (symbol excluded/removed from oracle)
Deleted stale allow-list entries whose symbol no longer exists in the reference oracle
(python-only subsystems already excluded: RAG/search backend, mcp_gateway daemon, CLI,
livewire, historical skill variants). The new SURFACE-DIFF dead-entry gate enforces this.
Coordinated-With: porting-sdk@fix/agentbase-mixin-flatten-fold
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* java: fold accessors + exclude ctor/dunder + emit oracle-gated DTO fields
Idiom-fold pass (idiom_reaudit_brief): allow-list entries are for genuine
capability differences only, never idiom. Fold idiom at the enumerator so the
diff sees the same surface.
Surface + signature enumerators (lockstep):
- DTO-field emission: generated read-side payload classes
(swml_verbs_generated / post_prompt_generated / swaig_request_generated)
now emit the oracle-recorded typed composition FIELDS, oracle-GATED per class
(never the full field list — scalar wire fields the reference doesn't record
are not over-emitted). Reserved-word field escapes resolve via @SerializedName.
Retires 174 surface omissions by emission (RULES.md §2). Aborts loud if the
oracle wants a field the DTO doesn't declare.
- accessor->member fold: getX/setX/isX/hasX/withX collapse onto the reference
member X they re-express (read+write of a public reference field), when the
reference records X on the same (module, class) and X != the accessor name.
- ctor/dunder exclusion: __init__/__repr__/etc. dropped from emission when they
would be a port-only ADDITION (reference records no such dunder on that class).
Result: surface omissions 218 -> 44; surface additions 741 -> 695; signature
drift folds down to genuine port-only typed enum/handler/functional-interface
surface + convenience-on-different-class + the WebService.app omission (JDK
HttpServer, no ASGI-app handle). Both diff_port_surface.py and
diff_port_signatures.py exit 0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NqhUoqrbptHNS3cypq9s6t
* java: delete 46 redundant matched addition entries (accessor-fold wins)
After the accessor->member fold, 46 PORT_ADDITIONS.md entries name a symbol that
now matches the reference by name (getX folded to the reference member X that the
oracle records — FabricNamespace.addresses, RequestOptions.timeout, POM.sections,
WebService.security, etc.). The parity holds without the allow-list line, so the
entry is redundant cruft — delete it (RULES.md §2: idiom folded, not excused).
Surface additions 695 -> 649. diff_port_surface.py + diff_port_signatures.py
both exit 0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NqhUoqrbptHNS3cypq9s6t
* java: restore load-bearing Section.subsections addition line
One of the 46 deleted "redundant" addition lines (Section.subsections) was
also excusing the SIGNATURE gate's missing-port: the surface fold matches it
(getSubsections->subsections), but the JAR-reflected Section signature carries
no zero-arg subsections method, so the signature oracle's field-accessor member
is missing on the signature side. Restore the line with a precise surface-
matched/signature-absent rationale. Both diffs exit 0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NqhUoqrbptHNS3cypq9s6t
* feat(surface): emit the construction contract; drop 4 dead logger omissions
FIRST PORT TO ADOPT the construction contract (porting-sdk ALLOWLIST_DISCIPLINE.md
§10, emitter + diff landed in porting-sdk cf05021/37dcb2a). java is the prover.
WHY THIS EXISTS
489 `__init__` signature-omissions fleet-wide — 62 of them java's, 17% of its
signature ledger. Each one excuses a WHOLE constructor from comparison, because
`compare_param` matches params by POSITION and ignores names, which is meaningless
for a 22-param kwargs constructor vs a Java builder. So one line hid every
parameter at once: a port could silently drop `record_stereo` or `signing_key`
forever and no gate would notice.
WHAT THIS EMITS
A `construction` node: a NAME-KEYED set of configurable params per class, sourced
from (1) the class's own `__init__` where it has a public ctor, else (2) its
BUILDER's setters — because in Java the builder setters ARE the construction
parameter set. `_BUILDER_CONSTRUCTS` binds AgentBaseBuilder→AgentBase and
RelayClientBuilder→RelayClient. The setter names were ALREADY canonical snake_case
(translate_method_name), so only the binding was missing — no name mapping needed.
123 classes emitted. Purely additive: 1,470 new lines, zero existing signatures
changed.
WHAT IT IMMEDIATELY REVEALED (369 findings, previously invisible)
construction-missing-param 240
construction-extra-param 66
construction-required-flip 45
construction-missing-class 16
construction-type-mismatch 2
On AgentBase alone — 11 reference construction params a Java developer CANNOT set
today: agent_id, check_for_input_override, config_file, default_webhook_url,
enable_post_prompt_override, native_functions, schema_path, schema_validation,
suppress_logs, token_expiry_secs, use_pom. Plus basic_auth, which is the §7
typed-split row (reference `optional<tuple<string,string>>` == java
authUser + authPassword; Java cannot idiomatically express a 2-tuple — verified in
AgentBase.java:146,238 — so that one folds rather than being a gap).
These are REPORTED, not written to any ledger: per ALLOWLIST_DISCIPLINE §0a an
agent may not create an omission or an addition. They are implement-work and go to
the owner as findings.
`required` is deliberately compared: a Java builder setter is optional by
construction, so where the reference marks a param required (e.g. AgentBase.name)
the diff raises `construction-required-flip` rather than silently accepting an
under-specified construction. That is contract per the owner ruling and must not
vary between ports.
ALSO — 4 dead logger omissions deleted
AgentServer/SkillBase/SkillManager/SkillRegistry `.logger`, dead since the owner
ruling that logging is a MODULE-LEVEL capability (porting-sdk 7f2a67f surface +
37dcb2a signature). The other 8 ports did this in their fold lanes; java's was
held back because this branch was doing the construction work.
VERIFIED (real gates, sibling-adjacent checkout)
diff_port_surface.py ✓ 2718 symbols; 40 excused omissions, 650 excused additions
diff_port_signatures.py ✓ 1411 reference symbols, 7031 port symbols, 6098 excused
Both exit 0. No JAR rebuild needed — this changes the enumerator only, no Java source.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y1fEcuaivoN1poPGS75jAy
* fix(agent): wire the missing AgentBase/SWMLService construction params
The reference AgentBase.__init__ FORWARDS several params to collaborators
rather than storing them on self:
schema_path / config_file / schema_validation -> super().__init__
(SWMLService) [agent_base.py:205-207]
token_expiry_secs -> SessionManager [agent_base.py:247]
Java implemented AgentBase's own state but never wired that forwarding
chain, so a Java developer could not point an agent at a config file or
disable schema validation even though the SDK already shipped ConfigLoader
(used by SecurityConfig + WebService) — the capability existed but was
unreachable from AgentBase.
Wired through, per Java's builder idiom:
AgentBase.Builder — agentId, usePom, nativeFunctions, defaultWebhookUrl,
suppressLogs, enablePostPromptOverride, checkForInputOverride,
tokenExpirySecs (-> new SessionManager(secs)), and the three forwarded
to Service: schemaPath, configFile, schemaValidation.
AgentBase.build() now loads the config file's `service` section
(name/route/host/port), with explicit builder values taking precedence,
matching the reference's _load_service_config precedence.
Service — new 9-arg constructor carrying schemaPath/configFile/
schemaValidation; schemaPath now actually reaches SchemaUtils (it was
hardcoded null), and configFile reaches a real SecurityConfig, the same
collaborators the reference forwards them to.
SchemaUtils.getSchemaPath() — the reference reads
self.schema_utils.schema_path at agent_base.py:210.
Because Service now exposes the `security` SecurityConfig member the
reference records, the PORT_OMISSIONS line claiming Java's SWML service has
no such member is stale — deleted (implementing made it unnecessary).
Enumerator folds (idiom at the emitter, RULES.md §2 / ALLOWLIST §0 — no new
allow-list entries):
- surface: construction-param read/write accessors strip to the
`construction` contract that already compares them by name; getSecurity()
folds onto the reference's `security` attribute like WebService's.
- signatures: SWMLService.__init__ joins PREFER_FULL_OVERLOAD so the 1-arg
convenience ctor stops hiding every param; Java's convenience-overload
expression of "defaulted param" is re-applied as required:false.
Tests: AgentConstructionParamsTest — 16 tests ported from the reference's
TestAgentBaseInitialization / TestSWMLServiceInitialization, driving real
behavior (token expiry decoded off the minted token, native_functions and
default_webhook_url asserted on the rendered wire, config-file precedence,
schema path reaching SchemaUtils, config file reaching SecurityConfig).
Construction findings: AgentBase 17 -> 6, SWMLService 7 -> 3. All residual
are the §7 basic_auth typed-split row (authUser/authPassword) plus the
pre-existing env_provider/max_duration extras.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y1fEcuaivoN1poPGS75jAy
* fix(surface): class B2 — fold or implement all 109 construction read-backs
CONSTRUCTION-READBACK [java]: 109 -> 0 (all 217 caller-supplied params are
readable back). Zero new ledger entries; 71 DEAD PORT_ADDITIONS deleted.
ENUMERATOR FOLDS (60 of the 109). The B2 oracle now records a public __init__
attribute that is also a ctor param as a real class member, which invalidated
two stale premises in the enumerator:
- `_CONSTRUCTION_PARAM_ACCESSORS` stripped construction-param getters on the
reasoning that "the oracle does not enumerate scalar state, so there is no
member to fold onto". True before B2, false now. The strip is ORACLE-GATED:
an entry applies only when the oracle does NOT record the underlying field,
so it degrades automatically as the oracle grows. 3 entries went inert.
- `_AI_CHAT_MEMBER_OVERRIDES` pinned AIChatClient/AIChatError to member lists
that excluded url/code/message — all three are B2 members now.
Plus per-class renames the generic same-name fold cannot reach: getSpace->host,
getName->function_name (DataMap), isNumberedBullets->numberedBullets (the
reference declares that attribute in camelCase verbatim; it is the wire key),
getServerMessage->message, SkillBase.bind->__init__.
The signature enumerator never applied `_SURFACE_METHOD_ALIASES` at all, so the
same symbols were green on surface and `missing-port` on signatures (friction
warning #1). Wired it in, gated on the NARROWER signature oracle so rows whose
target only the surface records (__call__, __getattr__) do not fire.
REAL DEFECTS FOUND AND FIXED:
1. SessionManager HMAC key disagreed with the reference — the same defect cpp
hit. The reference keys with the secret_key STRING's bytes
(self.secret_key.encode(), session_manager.py:79,152) and defaults it to a
64-char hex string; java stored 32 RAW bytes, so tokens were not
interoperable. The byte[] overload's own docstring admitted the mismatch.
secret_key is now a String; the byte[] overload decodes as UTF-8.
2. RelayError discarded the raw server message — it passed only the decorated
"RELAY error {code}: {message}" to super(), while the reference preserves the
undecorated value as self.message.
3. Call never carried project_id/context/segment_id. The event frames already
carried all three; the port read none of them. Populated at all three
construction sites.
4. AgentServer had no log_level param at all.
5. Prefabs discarded caller config: ConciergeAgent dropped services /
hours_of_operation / special_instructions, FAQBotAgent dropped persona /
suggest_related, SurveyAgent dropped survey_name / brand_name / max_retries /
introduction. Each is now stored AND rendered into the prompt, so the value
has effect rather than being dead state.
6. SkillBase never received the agent or retained its params (the reference
passes both to __init__). Added bind(agent, params), called by SkillManager
immediately before setup() — the reference's construct-then-setup ordering.
7. SWMLService host/name/port/route were PROTECTED fields with no reader.
Also fixed two pre-existing gate failures found on the way:
- pom.Section was absent from `signalwire.pom.pom` in the SIGNATURE dict: the
name-keyed class->module map resolved the colliding simple name `Section` to
a generated DTO module. The surface enumerator already had the FQN pin; the
signature table did not. 5 signature drifts cleared.
- The accessor fold ate `set_native_functions`: the reference files
native_functions on AgentBase but set_native_functions on AIConfigMixin, and
the "accessor is itself a reference member" guard was same-class only. It
now consults the whole agentbase-family (friction warning #5).
VERIFICATION (all six, before commit)
diff_port_surface.py exit 0 — 2758 symbols
diff_port_signatures.py exit 0 — 6399 excused divergences
suites/doc_truth.py exit 0 — all 8 rules PASS
suites/behavioral.py ENVELOPE + SWAIG-HTTP-INVOKE fail, both identical
at HEAD (parked 4.2 request-options; no
swaigHttpDump gradle target). WAIT-LIVENESS was red
at HEAD and is green now.
construction_readback.py exit 0 — 217/217
scripts/run-tests.sh 2219/2219 pass
The native-name sidecar needed only regeneration — java already ships the
producer, so the fold->doc drift resolved with no doc rewriting.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y1fEcuaivoN1poPGS75jAy
* ci(security): enforce SECRET-SCRUB-LIVE nightly — the credential-hygiene leg was never run
java already shipped the SECRET-SCRUB-LIVE fixture (SecretScrubDump.java + the
secretScrubDump Gradle task), but no gate ever invoked it. run-ci wired only
SECRET-SCRUB — the STATIC leg, which greps the relay source for the
raw-frame-log SHAPE. Those are two different rules: the static grep cannot see
what the client actually WRITES at debug level, so a credential reaching the log
by any path the grep does not model (a separate handler line, an interpolated
field, a wrapper that re-serializes the frame) was unobserved.
Adding SECRET-SCRUB-LIVE to BEHAVIORAL-NIGHTLY makes the RUNTIME property gated:
the fixture drives the real RelayClient through a live WebSocket connect plus an
inbound signalwire.authorization.state re-auth frame at SIGNALWIRE_LOG_LEVEL=
debug with the corpus sentinels (project=PJ-TESTLEAK, token=PT-TESTLEAK,
authorization_state=AENC-TESTLEAK), captures the process's own stdout+stderr,
and asserts no sentinel appears verbatim. What is now proven that was not
before: java's debug log provably contains no live credential, measured from the
captured output rather than inferred from the source.
Nightly is the rule's declared tier (it needs a live debug-level relay drive),
so it joins the existing BEHAVIORAL-NIGHTLY line next to WAIT-LIVENESS/
RELAY-LIVENESS rather than the per-PR set.
The gate is GREEN on java as shipped — no port fix was needed. java stores the
re-auth blob without logging it (RelayClient.java:1203) and scrubs both frame
log sites, matching the python reference.
NON-VACUITY PROVEN, not assumed. Reintroducing the exact defect the rule exists
to catch — a debug line interpolating the sentinel token into the log inside the
fixture's drive window — turned the gate RED with
`token oracle: {"leaked": false} java: {"leaked": true}`, then removing it
returned it to green. A fixture that reported leaked=false without driving
anything would not have moved.
VERIFICATION
python3 porting-sdk/scripts/suites/behavioral.py --port java --repo . \
--rules SECRET-SCRUB-LIVE exit 0, PASS
./gradlew -q secretScrubDump
{"project":{"leaked":false},"token":{"leaked":false},
"authorization_state":{"leaked":false}}
env SW_CI_TIER=nightly bash scripts/run-ci.sh
[BEHAVIORAL-NIGHTLY] behavioral suite, nightly rules
(WAIT-LIVENESS/RELAY-LIVENESS/SECRET-SCRUB-LIVE) ... PASS
(plus SURFACE/DOC-TRUTH/TEST/GEN/BEHAVIORAL/PACKAGE-NIGHTLY/FMT/LINT PASS)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GFKJhLvfV8yGrASwqxdgaf
* ci: wire TYPE-EROSION at the current count, so the number can only shrink
A port emitting `any` silently satisfies every type the reference declares —
compare_param treats `any` on EITHER side as matching anything, and says so in its own
docstring. That is a fair accommodation for a dynamic language and an unlimited opt-out,
and until now nothing counted how much of it there was.
It is not academic. ConciergeAgent.__init__ declares
hours_of_operation: optional<dict<string,string>> in BOTH its method signature and its
section-10 construction node. go shipped a single STRING — per-label hours unreachable,
and global_data carrying a string where every other port carries a map — with no gate red
anywhere. The oracle stated the type correctly and nothing enforced it.
Wired per port at its measured count: php 176, cpp 122, ruby 97, rust 55, typescript 45,
dotnet 19, go 16, java 13, perl 13. Total 556 slots where a reference declaration is
currently unenforceable.
RATCHET, DELIBERATELY, NOT A HARD GATE. Failing nine ports at once would produce nine
allow-list entries, which is the exact opposite of the goal. Banking the current number
means the count can be driven down on purpose while it can never grow by accident.
Placed immediately after each port's SURFACE gate, because it reads the
port_signatures.json that enumeration writes. cpp needed a hand edit: it runs gates
SERIALLY via `run_gate "NAME" "desc" cmd` rather than the `sched_gate` DAG the other eight
use, so a scripted insert anchored on sched_gate correctly skipped it rather than
producing a silently dead line.
Verified all nine: bash -n clean, and the gate PASSES at its banked ratchet. Non-vacuity
proven rather than assumed — lowering a ratchet by one makes go, java and dotnet each exit
1 naming the regression, so the gate fires instead of decorating.
* ci: install the porting-sdk scripts' declared dependencies before running them
Completes the fix for dotnet's "311 Python symbol(s) missing from port" — a CI-only red
that exited 0 locally against byte-identical inputs, against a port that was correct.
Root cause: enumerate_surface.py reaches the reference oracle through generate_rest.py,
which needs PyYAML. The CI interpreter had none, the import raised ModuleNotFoundError, a
bare `except Exception: return None` swallowed it, the oracle loaded EMPTY, and 266
oracle-gated members silently failed to emit. Proven by md5-ing every diff input (only the
REGENERATED port_surface.json differed) and then directly: 3.13 emits 1530 members, a
yaml-less interpreter 1264, and the 266 lost are exactly the gated ones.
porting-sdk 3eee516 added scripts/requirements.txt as the single declaration plus
check_script_deps.py to keep it honest. This is the consumer half: every workflow that
runs those scripts now installs from that manifest.
17 workflows across 9 ports — each port's surface-audit and doc-audit, plus go's
multi-os. Every one previously ran the gate scripts and installed NOTHING, which is why
the failure was one forgotten dependency away in nine repos simultaneously.
perl/live-smoke.yml was deliberately SKIPPED: it has no setup-python step to anchor to,
so an inserted pip line could not be guaranteed to have an interpreter. Flagged rather
than forced.
Placement verified programmatically, not by eye, because I got it wrong twice: the first
attempt split the setup-python step in half (install landed between `- name:` and its
`uses:`), and the second placed it AFTER the enumerate step in 7 workflows and BEFORE
setup-python in 4. Both passed `yaml.safe_load` — structurally valid and semantically
useless — which is exactly why the check asserts ORDER: install must come after any
setup-python step and before the job's first run step. All 17 now satisfy that, with no
duplicates and valid YAML across all 63 workflow files.
* fix(gate): TYPE-EROSION raced the surface suite — I broke four ports' CI
MY REGRESSION, from wiring the gate without testing it in CI's scheduling order. go, perl,
rust and cpp went red with
[TYPE-EROSION] ... FAIL: exit 1
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
against a port_signatures.json that is valid and committed. The surface suite REGENERATES
that file in place, and I declared the new gate with no scheduler resource — so it ran
CONCURRENTLY with SURFACE (which holds res=surface) and read the file mid-write.
Two fixes, because either alone is insufficient:
1. SCHEDULER (the real remedy): TYPE-EROSION now takes the same resource as each port's
SURFACE gate — go/php/perl/dotnet res=surface, java res=gradle — so they cannot overlap.
typescript, ruby and rust declare NO resource on SURFACE, so serialisation is not
available there, which is why (2) is required.
2. THE GATE tolerates it. On an unparseable snapshot it now SKIPS with the byte count, the
decode error, and the instruction to give it SURFACE's resource — rather than dying.
Deliberately a SKIP and not a pass: 0 erosions measured from a half-written file is a
FALSE GREEN, which is worse than the red it replaces.
Verified all four paths rather than assuming: a normal run still exits 0; an empty snapshot
skips with exit 0 and an explanatory message; the ratchet still FIRES (exit 1) when the
count exceeds it, so the skip did not blunt real detection; the fleet total is unchanged at
556; and bash -n passes on all nine run-ci scripts.
Worth naming the pattern, since it is the third instance today: a gate that reads a
regenerated artifact must either hold that artifact's lock or refuse to guess. The other two
were the empty-oracle fallbacks that produced phantom missing-symbol counts. Same shape —
read a file someone else is writing, then report the result as if it were the truth.
* feat(swml): adopt the 169-def schema — ai_sidecar verb + typed RingbackConfig
Coordinated pass against porting-sdk 3435180 (169 $defs = the prior 167 +
RingbackConfig + AiSidecar). Two upstream changes land together on purpose: doing
them as two separate 9-repo passes would burn the fan-out twice.
1. RUNTIME schema — this SDK SHIPS schema.json for its own validation, so a stale
copy means the shipped library validates against a schema with no ai_sidecar and
no RingbackConfig. Re-vendored byte-for-byte from canonical (copied, not
re-serialized: the x-sdk-* markup is load-bearing and a re-serialize would also
churn formatting).
2. GENERATED types — regenerated from CANONICAL, which is what the generators read
(NOT the vendored copy). That is why GEN-FRESH-SWML went red the moment canonical
moved: it compares generated code against canonical.
New surface: the ai_sidecar verb (live_transcribe plus an LLM/SWAIG/MCP loop) with
AiSidecarConfig carrying all 12 fields — real direction/customer_role enums and SWAIG
resolved through its $ref. RingbackConfig replaces what was collapsing to an untyped
map: connect's ringback is now `array | RingbackConfig` with all 9 properties typed.
VERIFIED, by running the gates rather than reading the diff:
* GEN suite: all rules PASS (this port, and 9/9 across the fleet)
* SURFACE-DIFF: exit 0 — the 6 drifts the oracle correctly raised
(AiSidecarConfig.SWAIG + 5 ringback slots) are now implemented, not excused.
No omission entry was added: a real missing capability is never idiom.
* vendored copy asserted equal to canonical, 169 defs, x-sdk-* count 5
* behaviorally exercised against the vendored schema (go, ValidateVerb):
ai_sidecar{prompt,lang,direction,customer_role} -> valid;
missing `prompt` -> correctly rejected; customer_role "nobody" -> correctly
rejected; ringback object form -> valid.
port_signatures.json is regenerated and committed — SURFACE-FRESH fails otherwise.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GFKJhLvfV8yGrASwqxdgaf
* chore(surface): regenerate port_surface.json for the ai_sidecar/RingbackConfig surface
Follow-up to the 169-def schema adoption in this PR. There are TWO per-port audit
artifacts and the schema commit only refreshed one: port_signatures.json was
regenerated, port_surface.json was not — so surface-audit went red naming the same 4
symbols the signatures gate had already accepted:
signalwire.core.swml_verbs_generated.AiSidecar
signalwire.core.swml_verbs_generated.AiSidecarConfig
signalwire.core.swml_verbs_generated.AiSidecarConfig.SWAIG
signalwire.core.swml_verbs_generated.RingbackConfig
Regenerated with the same invocation CI uses. Verified: diff_port_surface.py exits 0
against the regenerated porting-sdk/python_surface.json (porting-sdk c08cb73), for this
port and all nine.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GFKJhLvfV8yGrASwqxdgaf
* test(swml): stop asserting a frozen verb HEADCOUNT — assert the schema loaded instead
ai_sidecar makes 39 verbs, so every `== 38` assertion in the fleet broke at once. The fix
is not 38 -> 39: that just moves the same trap one verb further out, and the next upstream
verb breaks it again.
WHY THESE ASSERTIONS SHOULDN'T EXIST AS EXACT COUNTS: the python reference has NO
equivalent assertion. swml_service.py only LOGS the count
(`log.debug("found_verbs_in_schema", count=len(verb_names))`). So an exact-count check is
not parity with the reference — it is port-invented brittleness that fires on every
correct upstream change and has never caught a real defect. Notably php/perl/dotnet/cpp
already used `>= 38` and were unaffected, which is the shape that survives.
What is actually worth pinning, and what these now assert:
* the schema is not TRUNCATED (>= 38 verbs — catches a failed/partial load, the real bug)
* the count agrees with the names actually extracted (catches a count/list mismatch)
* ts: names are unique; and the pre-existing loop that every schema verb has a builder
method — that loop was always the real test, the headcount was noise.
DELIBERATELY LEFT EXACT: SwmlTest.java:358 `assertEquals(38, ...getVerbs().size())`. That
counts the 38 verbs the test itself explicitly calls, so it is a genuine invariant of the
test body, not a schema headcount. Changing it would weaken a real check.
Verified per port by running the tests, not by reading:
go pkg/swml ok
ruby 64 runs, 117 assertions, 0 failures
ts 2 files, 84 tests passed
java SchemaUtilsTest + SwmlTest, 0 failures
Scope note: all of go's THREE red checks (test, doc-audit, windows) were this same pair of
tests — doc-audit and windows both run `go test ./...`. Likewise ruby's REST-COVERAGE red
was the same single test failure surfacing again, because that gate runs the whole suite.
Four ports, three gates, one root cause.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GFKJhLvfV8yGrASwqxdgaf
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>1 parent 70e13af commit 4dd76a7
43 files changed
Lines changed: 7779 additions & 2072 deletions
File tree
- .github/workflows
- scripts
- src
- main
- java/com/signalwire/sdk
- agent
- contexts
- core
- agent
- prompt
- tools
- prefabs
- relay
- security
- server
- skills
- swml
- generated
- web
- resources
- test/java/com/signalwire/sdk
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
52 | 52 | | |
53 | 53 | | |
54 | 54 | | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
55 | 65 | | |
56 | 66 | | |
57 | 67 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
48 | 58 | | |
49 | 59 | | |
50 | 60 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 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 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
1 | 48 | | |
2 | 49 | | |
3 | 50 | | |
| |||
0 commit comments