Rebased a33685e onto reviewed mainbbc335c; local tests/vet and actual CI passed. Canonical claude-fable-5-1 APPROVE full verdict below. Nonblocking diagnostic/comment suggestions left out of this narrow change; existing error classification retained. Real regression observed plaintext dial beforefix; zero dial afterward. Rechecking current Memoria compatibility before merge.
Evidence gathered. Writing the verdict.
Verdict: APPROVE, with two non-blocking notes.
Verified independently: the worktree's branch ref resolves to a33685e, and the on-disk transport.go, transport_test.go and new transport_scheme_test.go match the diff in /tmp/oficina-suite-https-review/diff.txt line for line. I could not run go test or go vet in this session, so "tests/vet green" rests on the submitter's report.
Smallest fix. Three lines at transport.go:49 in the wrapping RoundTrip, placed before the host guard, so the scheme is refused before CheckSiblingHost and before the inner http.Transport ever dials. Because url.Parse lowercases the scheme, and an empty or exotic scheme also fails the equality test, there is no bypass through case or a missing scheme. Nothing else in the client changes. This is the minimal correct placement.
Error classification. Returns the existing bare ErrContractBreach sentinel from s3.go:20. Reachable through http.Client.Do because Go wraps transport errors in *url.Error, which unwraps, so the new test's errors.Is check is sound. Consumer-side, memoria's only special handling of that sentinel is the 403 leak path in sibling/feed.go:87, which keys on errors.Is, so a cleartext refusal would surface as "sibling broke the contract" rather than be absorbed. Acceptable as the constraint was stated, though see note 1.
Test evidence.
- Regression
TestSiblingClientRefusesPlaintextBeforeSendingCredentials: authenticated POST with a bearer header to a valid same-team host on plain HTTP. Against main,recordingDialerwould recordacme.docs.oficina.works:80and the test's zero-dial assertion fails. After the fix it asserts botherrors.Is(err, ErrContractBreach)and zero dials. Correct before/after shape. - Positive tests unchanged:
TestSiblingClientReachesSameTeamHostandTestSiblingClientAllowsMatchingHostOverridestill usehttps://and still expect exactly one dial. - Redirect test now uses
httptest.NewTLSServer, pinsRootCAsto the server certificate and setsServerNameto the cert's first DNS name (example.comin Go's test cert). NoInsecureSkipVerify. The dialer still routes every name to the server, so TLS verification is real. The newelse ifasserting"refusing to follow a redirect"is a genuine strengthening: on main, mutation 10 inmutations.py(CheckRedirect returns nil) would have survived this test, since the second hop toacme-evilwas refused by the host guard with dial count still 1 and err non-nil. Now the error text must come fromCheckRedirect, so the mutation is caught.
Consumer compatibility. Memoria's checked-in consumer in the tensorx-provider worktree already rejects non-https base URLs at config load (config_siblings.go:108) and pins suite at v0.0.0-20260905215529-60b01a8a0d6d, so no existing consumer reaches the new check. The main memoria checkout has no sibling package yet. No existing consumer leak is implied and none is claimed.
Non-blocking notes.
- The bare sentinel gives an operator "sibling broke the contract" with no mention of scheme, and semantically a cleartext URL is a consumer configuration bug, not a sibling misbehaving. Wrapping as
fmt.Errorf("%w: %q is not https", ErrContractBreach, scheme)would match thes3.go:40style and keeperrors.Isintact without new UI copy. Your call, as you constrained this deliberately. mutations.pyhas no entry for the new check, and the three lines carry no comment, which departs from this file's house style. The new test would catch removal of the check on its own, so this is hygiene only.