Skip to content

docs(W7): no-paste-review rule for Option B handoff (W7 workstream precondition) - #43

Merged
gHashTag merged 1 commit into
mainfrom
w7/docs/collab-refinement
Jul 5, 2026
Merged

gHashTag merged 1 commit into
mainfrom
w7/docs/collab-refinement

Conversation

@gHashTag

@gHashTag gHashTag commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Purpose: ratify W7 collab choice (Option B — divide by concern, not by host) and lock in one mandatory refinement to its handoff protocol before W7 workstreams open.

Refinement

Closes W6.2 weak-point 1.8 (draft-lived-in-sandbox, review-went-by-paste — the mechanism behind the Vec-grep-anchor, differential-narrative-anchor, and all-modes-anchor episodes documented in docs/W6_CODEGEN_AUDIT_2026-07-05.md §Anchor-bias record).

Rule tiers:

  • Doc-layer (w7/docs/*) — mandatory: approve only against committed text in the branch. git fetch origin <branch> && git show <branch>:<path> (or gh api /contents/) before approve. Paste is navigation context, not approval object.
  • Compiler-layer, testing-layer — preferred: same rule; ≤10-line-diff exception for fast clarifications, but final approve is against committed text.
  • Enforcement: reviewer cites SHA in PR comment (Reviewed against <sha> / Approved at <sha>) so approve is reproducibly bound to file state.

Why this comes before any W7.x workstream

Option B's whole strength is layer separation with explicit dependency-through-Base. Without the no-paste-review rule, the doc-layer (W7.5 paper §4.5 companion, W7.7 trust ledger) — the two highest-stakes workstreams of W7 — would inherit the same review-by-paste failure mode that produced the anchor-bias episodes of W6.2. The refinement makes that impossible by construction.

Scope

  • One file: docs/W7_COLLAB_OPTIONS.md
  • 6 lines added to Option B handoff
  • No workstream started yet; this is the ratification PR

phi^2 + phi^-2 = 3

Ratifies W7 collab choice: Option B (divide by concern, not by host),
with one refinement mandatory before workstream start.

The refinement closes W6.2 weak-point 1.8 (draft-lived-in-sandbox,
review-went-by-paste). In W7 the doc-layer specifically cannot approve
against paste — approve is only valid against committed text in a
w7/docs/* branch, fetched via git or gh api. Compiler and testing layers
follow the same rule as preferred, with a 10-line-diff exception for
fast clarifications. All approves must cite a SHA so the review is
reproducibly bound to file state, not to transient chat paste.

This is the anti-anchor discipline of PR #42 promoted from ad-hoc
practice to a written rule for the whole W7 cycle.

phi^2 + phi^-2 = 3
@gHashTag
gHashTag merged commit 9578bc3 into main Jul 5, 2026
2 checks passed
gHashTag added a commit that referenced this pull request Jul 5, 2026
Adds two sibling rules to no-paste-review in docs/W7_COLLAB_OPTIONS.md:

1. SHA-advance re-review rule — approve binds to cited SHA; branch advance between approve and merge requires explicit re-confirm with delta bullet-list. Silent SHA-swap forbidden. Applies to all layers.

2. External-dep timer rule — PR blocked on external dependency must have terminal-event triggers (won't-fix / closing PR / explicit reject) + backstop timer (default 14d from upstream issue publication). Whichever comes first.

Together with no-paste-review (PR #43), these form the temporal-precision triad governing W7 review workflow:
- no-paste-review: spatial locality (review \ne paste)
- SHA-advance: temporal drift within PR (approve \ne merge-state)
- external-dep timer: temporal drift outside PR (draft \ne infinite wait)

GLM peer-review @ e1d29f3: APPROVE. First application of SHA-advance rule to itself (head unchanged since approve).

phi^2 + phi^-2 = 3
gHashTag added a commit that referenced this pull request Jul 5, 2026
…2; NAMED_CONST] (#47)

W7.3 grammar-expansion increment #1 — collection-typed params using [u32; NAMED_CONST] syntax matching the tri-net/specs audit corpus.

Path-confirmed via t27c gen-rust: 1000/1000 gen-rust succeeded, 1924 [u32; NAMED_CONST] in .t27 input → 1924 Vec<> in Rust output, exact one-to-one. W6.2 Class 2 defect surface (Vec<> param-position) exercised by construction.

Independently verified by GLM re-review at 9bbc103:
- Documented 1000-spec sweep: 1924 Vec<> in gen-rust output
- Independent t27c run on specs/anomaly_detector.t27: rc=0, 7 Vec<> confirmed

Discipline chain observations (this PR):
- Anchor #4 recorded: any claim verified against ground truth requires scoping the verification tool to the same corpus as the claim.
- SHA-advance rule (PR #45): applied at 247427d → 9bbc103 with delta bullet-list in PR body.
- No-paste-review rule (PR #43): GLM approved against committed body text with SHA citation.

Commits (post-squash provenance):
- 2080510  fix(W7.3): word-boundary anchor normalize_ast regex
- 247427d  feat(W7.3): grammar-expansion target #1 — collection-typed params (Zig-style, OBSOLETED)
- 9bbc103  fix(W7.3): rewrite collection-params to [u32; NAMED_CONST] per audit-corpus ground truth

Base: main @ 3272583 (PR #46, E1+E2 frozen baseline). E3 still timer-blocked (backstop 2026-07-19 12:24 UTC per PR #44).

phi^2 + phi^-2 = 3
gHashTag pushed a commit that referenced this pull request Jul 23, 2026
…NAT brick)

#42 gave each side its public address (STUN), #43 punched between two KNOWN
ports. The missing glue: serialize the candidate LIST so the two sides can
exchange it over a signaling/rendezvous channel, and orchestrate a real connect
that probes ALL of the peer's candidates and nominates the one that answers (a
NAT may silently drop some pairs). IceSession.swift does both, pure + standalone,
reusing HolePunch's probe/ack codec and priority:
  * Ice.encode/decode: [count:2][kind:1][port:2][ipLen:1][ip] per candidate,
    bounds-checked on parse;
  * Ice.connect(localPort, remote[]): bind one socket, probe every remote for the
    whole window, ack observed sources, nominate the highest-priority remote that
    actually answered; report the bound port as the media socket to hand off.

Verified in smoke/harness/ice_session.swift (10th verify.sh test, verify: 10
passed, 0 failed): serialization round-trips + rejects garbage; and TWO real
in-process sessions exchange serialized blobs and connect over loopback UDP while
correctly discarding a decoy candidate (192.0.2.2, RFC 5737 unroutable) that
never answers. Ran 3x, 3/3 deterministic.

Nominate by "did it ACK", never by priority alone — that is what makes a dead
higher-priority candidate get skipped instead of selected. Do not early-exit on
first success (strands the peer mid-handshake); run the full window, keep acking.

Boundary: loopback proves serialize/exchange/connect/nominate over real UDP, NOT
traversal of a real NAT (two separate NATs). The three bricks (STUN, punch,
session) are harness-proven; wiring connect() into CallManager before the media
socket, fed by the room's exchanged candidates, is the integration step.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
gHashTag pushed a commit that referenced this pull request Jul 23, 2026
…fore building it

The three NAT bricks (STUN #42, punch #43, ICE session #44) all assume the peers
already hold each other's candidate lists. Whatever rendezvous carries that list
must NOT be trusted to read or forge it: an injected candidate redirects the call
to a machine the attacker controls (classic ICE candidate-injection / call hijack;
WebRTC blocks it with signed SDP + a DTLS fingerprint). This is a prerequisite for
a rendezvous, not an afterthought — shipping candidate exchange unsealed is a
call-hijack hole.

CandidateOffer.swift (pure; reuses MeshCrypto.inviteAuthKey + Ice serialization):
seal [version][tiebreaker:8][expiry:8][Ice candidate list] under a room-derived
key, with an expiry so a captured offer cannot be replayed later, and an ICE
controlling/controlled tiebreaker. The offer key is domain-separated from the
invite key by one HKDF step so a candidate offer and an invite can never be
cross-interpreted.

Verified in smoke/harness/candidate_offer.swift (11th verify.sh test, 13 checks,
verify: 11 passed, 0 failed): honest round-trip recovers the list + tiebreaker;
wrong room passphrase -> nil (confidential); flipped auth-tag byte -> nil
(unforgeable); the offer does NOT open under the raw invite key (domain
separation); past-TTL -> nil (stale, un-replayable); role resolves oppositely for
the two peers. Clock is injected so expiry is deterministic; only static room-key
derivation is used, so no MeshCrypto() is built and the Keychain is untouched.

Four harness-proven NAT/exchange modules now exist (StunClient, HolePunch,
IceSession, CandidateOffer); none are in project.yml / wired into CallManager yet —
that integration, fed by a rendezvous carrying these sealed offers, is next.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
gHashTag pushed a commit that referenced this pull request Jul 23, 2026
…r -> sealed offer)

Four waves built harness-proven but UNWIRED NAT modules (StunClient #42, HolePunch
#43, IceSession #44, CandidateOffer #45). Proven-in-a-harness is not
proven-in-the-product, and four modules outside the binary is a growing debt. This
wave puts them in the shipping Mac app and proves they run there, WITHOUT touching
the working same-subnet call.

  * project.yml: register the four modules + a new NatDiagnostics; xcodegen
    regenerated the tracked pbxproj (clean +20 lines, only the 5 files, no churn).
  * NatDiagnostics.run(): off-main at launch (.onAppear), gathers host + STUN
    server-reflexive candidates and seals a CandidateOffer under the current room,
    then logs it. Additive only — nothing in the call/media path changes.

Verified LIVE in the built binary (not a harness): launched with TRINET_LOG and
read back
  TRINET NAT: candidates host=["192.168.1.104"] srflx=182.232.218.171:59434
              -> sealed offer 83B (room=lobby)
so StunClient.hostCandidates + gatherServerReflexive (real public address via
Google STUN) + CandidateOffer.make all execute inside the app; the app did not
crash, so the working call is intact.

Deferred deliberately: the iOS embed (static file list is fragile) and the real
integration — deliver the offer via a rendezvous and run Ice.connect before the
media socket. The app now HOLDS its sealed candidate offer; delivering it and
connecting on it is next.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
gHashTag pushed a commit that referenced this pull request Jul 24, 2026
…mmetric NAT

Kernel NAT emulation is unavailable here (`sudo -n true` -> "a password is
required", so pfctl/dnctl are out). But the property that actually breaks hole
punching needs no root to model: a symmetric NAT allocates a different external
port PER DESTINATION, so the peer's probes arrive from an address that is NOT in
the candidate list it advertised — that list was learned from a STUN server and is
useless to us.

smoke/harness/symmetric_nat.swift models exactly that (the peer advertises a dead
port and speaks from another) and FAILED against the previous code: Ice.connect
only ever probed the advertised list, so it never nominated anything. A real gap,
found by building the adversarial case rather than assuming the design covered it.

Fix: learn PEER-REFLEXIVE candidates. A probe arriving from an unknown source is
added as a candidate, so it gets probed and can be nominated. 3/3 runs pass after
the fix; added as the 14th verify.sh test (verify: 14 passed, 0 failed). The iOS
Ice enum was re-mirrored so all six NAT enums stay byte-identical; both apps build.

CORRECTION to the previous commit's wording: the fd hand-off alone is NOT
"symmetric-NAT support". What makes a ONE-SIDED symmetric NAT work is
ack-to-the-observed-source (wave #43) + peer-reflexive discovery (this commit) +
the fd hand-off keeping that mapping alive. Symmetric-on-BOTH-sides still cannot
punch — neither side's first packet is admitted — and needs a relay (TURN-style).
That gap is real and remains open; do not read a one-sided test as more than it is.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.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