533 lines
21 KiB
Markdown
533 lines
21 KiB
Markdown
# 11 — Pitfalls, Anti-patterns, and What to Avoid
|
||
|
||
This is the prominent pitfalls chapter. Greenfield's known weaknesses,
|
||
anti-patterns, and design pitfalls are catalogued here so the user can
|
||
avoid making the same mistakes in their own Wayland-to-HTML compositor
|
||
(Odoo 20 + JS/Python). Each pitfall comes with: **what Greenfield does**
|
||
(with file/directory reference), **why it's a problem** (technical
|
||
reason), and **what the user should do instead** (concrete recommendation
|
||
for their JS/Python + Odoo 20 stack).
|
||
|
||
## Headline numbers
|
||
|
||
- **299** `TODO|FIXME|HACK|XXX|@deprecated|workaround` hits across non-vendored source (after excluding `examples/sdk/*` upstreams and `xtsb/protocol/xproto.xml`).
|
||
- **13 packages** all pinned to `"version": "1.0.0-rc1"` — no `CHANGELOG.md`, no `SECURITY.md`, no `CONTRIBUTING.md`, no issue/PR templates.
|
||
- **1 commit** visible on `master` (`6c578f4`, "Fix typo in code snippet (#161)", 2025-10-15) — implying the public repo is a release snapshot, not the live dev branch.
|
||
- **3 native Node `.node` addons**, all Linux-x64 only.
|
||
- **0** uses of `OffscreenCanvas` or `transferControlToOffscreen` anywhere in the compositor.
|
||
- **2** Web Workers used — and only for the WASM H.264 *fallback* decoder path. The primary WebCodecs path uses none.
|
||
|
||
## Catalogued pitfalls (A–U)
|
||
|
||
### Critical (will bite you on day 1)
|
||
|
||
#### A. Native Node addons
|
||
|
||
**What Greenfield does**: Three Node-API addons:
|
||
- `wayland-server-addon.node` (1231 lines C, wraps patched libwayland-server)
|
||
- `proxy-encoding-addon.node` (290 lines C, wraps GStreamer)
|
||
- `proxy-poll-addon.node` (127 lines C, wraps libuv `uv_poll_t`)
|
||
|
||
All Linux-x64 only, built with CMake + Ninja. ABI-locked to Node major
|
||
versions.
|
||
|
||
**Why it's a problem**:
|
||
- Linux-x64 only — no ARM64 support
|
||
- ABI breaks on Node major upgrades
|
||
- Hard to ship — must rebuild for each target platform
|
||
- Hard to debug — C N-API debugging requires core dumps
|
||
- GStreamer integration is via C, not Python
|
||
- No Python story
|
||
|
||
**What you should do instead**: Use `pywayland` (cffi bindings to
|
||
libwayland-server), `python-xkbcommon`, and GStreamer's Python bindings
|
||
(`gi.repository.Gst`). One Python process can replace the entire
|
||
`compositor-proxy-cli`. See `10-porting-to-odoo-js-python.md` for details.
|
||
|
||
#### B. rc1 with no policy
|
||
|
||
**What Greenfield does**: All 13 packages at `1.0.0-rc1`. No CHANGELOG,
|
||
no SECURITY.md, no CONTRIBUTING.md.
|
||
|
||
**Why it's a problem**: Breaking changes can land silently. The
|
||
maintainer's roadmap is unclear.
|
||
|
||
**What you should do instead**: Pin to a specific commit hash if you fork
|
||
it. Track upstream commits manually. Plan for the eventuality that
|
||
1.0.0 final may break APIs.
|
||
|
||
#### C. Encoding rigidity
|
||
|
||
**What Greenfield does**: Three H.264 GStreamer pipelines (x264/nvh264/
|
||
vaapih264) with hand-tuned magic constants:
|
||
- `bitrate=12000 vbv-buf-capacity=160 bframes=0 rc-lookahead=0`
|
||
- VA-API is "experimental"
|
||
- AMD (`vah264enc`) absent
|
||
- PNG fallback at hardcoded ≤256×256 threshold
|
||
- Browser-side hardware decode is explicitly disabled in `Session.ts:93`
|
||
with a FIXME "hardware decoding frame offset is wrong"
|
||
|
||
**Why it's a problem**:
|
||
- No adaptive bitrate — video plays at 12 Mbps regardless of network
|
||
conditions
|
||
- No AMD support — limits hardware encoder options
|
||
- Browser decode is software-only — wastes GPU
|
||
|
||
**What you should do instead**:
|
||
- Build an adaptive multi-codec encoding router (PNG for ≤64×64 or static,
|
||
VP8/H.264 for mid-size, H.264/AV1 for video)
|
||
- Drive the router by the `EncoderFeedback` measurement Greenfield already
|
||
produces but doesn't consume
|
||
- Use `pykcp` or similar for congestion-aware bitrate adaptation
|
||
- Enable browser hardware decode (`prefer-hardware`) — fix the "frame
|
||
offset is wrong" bug
|
||
|
||
#### D. Wire protocol has no schema version field
|
||
|
||
**What Greenfield does**: No version prefix on any binary message:
|
||
- `EncodedFrame` (FRAME channel)
|
||
- `RemoteOutOfBandChannel` (PROTOCOL channel OOB)
|
||
- The FD-channel in `RemoteAppLauncher.ts:612`
|
||
- Undocumented magic close codes (`4200`, `4201`, `4403`, `4001`, `1005`)
|
||
|
||
All break-lockstep — if you change the wire format, both sides must
|
||
upgrade simultaneously or messages will be misinterpreted.
|
||
|
||
**Why it's a problem**:
|
||
- Cannot evolve the protocol without breaking all clients
|
||
- Cannot detect version mismatches
|
||
- Hard to debug — close codes are magic numbers
|
||
|
||
**What you should do instead**:
|
||
- Prefix every binary message with `u16 protocolVersion`
|
||
- Document every close code in a comment
|
||
- Use JSON `{"v":1, "type":"..."}` for the control plane
|
||
- Build a feature-flag system so old clients can negotiate capabilities
|
||
|
||
#### E. Main-thread decode + render
|
||
|
||
**What Greenfield does**:
|
||
- `VideoDecoder` runs on main thread
|
||
- WebGL context bound to `HTMLCanvasElement`
|
||
- No `OffscreenCanvas`
|
||
- No `Worker` in render path
|
||
- 0 uses of `OffscreenCanvas` or `transferControlToOffscreen` in the
|
||
compositor
|
||
- Only 2 Web Workers used (FFmpeg Wasm fallback), shared across surfaces
|
||
|
||
**Why it's a problem**:
|
||
- Jank on 1080p surfaces — main thread blocks on decode
|
||
- No way to recover if main thread stalls (input also blocked)
|
||
- Can't use `requestAnimationFrame` for compositing if main thread is busy
|
||
|
||
**What you should do instead**:
|
||
- Render in a Web Worker with `OffscreenCanvas` from day one
|
||
- Ship `EncodedVideoChunk` / `ArrayBuffer` transfers (zero-copy) to the
|
||
worker
|
||
- Use `VideoFrame` directly as a WebGL texture source — no `copyTo`
|
||
- Use multiple workers (one per output) for parallel rendering
|
||
|
||
### High severity
|
||
|
||
#### F. xkbcommon Wasm is multi-MB
|
||
|
||
**What Greenfield does**: `libs/compositor-wasm/xkbcommon/build.sh`
|
||
clones libxkbcommon 1.5.0, embeds the entire xkeyboard-config database
|
||
via `--embed-file`. Result: multi-MB WASM module loaded on every page.
|
||
|
||
**Why it's a problem**:
|
||
- Slow initial load
|
||
- Layout list parsed via fragile string matching
|
||
- Hard to update keyboard layouts without rebuilding WASM
|
||
|
||
**What you should do instead**:
|
||
- Use the browser's `navigator.keyboard.getLayoutMap()` API for basic
|
||
layout detection
|
||
- For full xkbcommon support, lazy-load the WASM only when needed
|
||
- Consider server-side keymap compilation (Python `python-xkbcommon`)
|
||
and ship only the result string to the browser
|
||
|
||
#### G. Clipboard edge cases
|
||
|
||
**What Greenfield does**:
|
||
- Hardcoded MIME list `['text/plain', 'text/html', 'image/png']`
|
||
- No primary selection (`wp_primary_selection_v1` not implemented)
|
||
- Change detection by blob size (false negatives on same-size different
|
||
content)
|
||
- Focus check at `DataOffer.ts:73` is a FIXME
|
||
- Browser-side clipboard polling (no `clipboardchange` event)
|
||
|
||
**Why it's a problem**:
|
||
- Middle-click paste doesn't work (Linux users expect it)
|
||
- Large clipboard content (e.g. copied image) is poorly handled
|
||
- MIME negotiation races
|
||
|
||
**What you should do instead**:
|
||
- Implement `zwp_primary_selection_device_manager_v1` from day one
|
||
- Use exact MIME matching, not substring
|
||
- For large clipboard content, use chunked transfer
|
||
- Watch for the `clipboardchange` event in modern browsers (currently
|
||
behind flags in Chrome)
|
||
|
||
#### H. XWayland/xtsb complexity
|
||
|
||
**What Greenfield does**:
|
||
- A full XCB reimplementation in TypeScript with its own 5800-line
|
||
`xproto.xml`
|
||
- The XWM is a hand-port of wlroots' `xwm.c` with FIXMEs pointing at
|
||
specific wlroots commits
|
||
- `XWindowManager.ts:746` literally says "this makes xterm close somehow"
|
||
- Title bars painted pixel-by-pixel in `XWindowFrame.ts`
|
||
|
||
**Why it's a problem**:
|
||
- 5800 lines of X11 protocol XML to maintain
|
||
- Hand-ported XWM logic is fragile and out of sync with upstream wlroots
|
||
- Magic workarounds in code
|
||
|
||
**What you should do instead**:
|
||
- Use `python-xlib` on the Python proxy side instead of porting xtsb
|
||
- Consider running XWayland with a minimal window manager (e.g. mutter
|
||
in kiosk mode) instead of implementing XWM yourself
|
||
- For XWayland apps only (no full X11 desktop needed), the XWM can be
|
||
much simpler than wlroots'
|
||
|
||
#### I. Adaptive bitrate feedback loop is wired but not consumed
|
||
|
||
**What Greenfield does**:
|
||
- `EncoderFeedback.ts` measures client decode duration at 1 Hz
|
||
- 4-byte feedback messages sent from browser to proxy
|
||
- Proxy receives them but does NOT use them to adapt bitrate
|
||
- `Channel.ts:181` has a TODO confirming the gap
|
||
|
||
**Why it's a problem**:
|
||
- Video plays at fixed 12 Mbps regardless of network conditions
|
||
- No way to recover from congestion
|
||
- Wasted bandwidth on slow networks
|
||
|
||
**What you should do instead**:
|
||
- Wire `EncoderFeedback` to a bitrate adaptation controller
|
||
- Use `GstElement.set_property("bitrate", ...)` to adjust x264enc/nvh264enc
|
||
- Implement `requestKeyUnit` plumbing (Greenfield has it end-to-end but
|
||
the browser never calls it)
|
||
|
||
#### J. Single-seat, single-session model
|
||
|
||
**What Greenfield does**:
|
||
- One seat per browser session
|
||
- One Node process per browser session (per `compositorSessionId`)
|
||
- No multi-seat support (e.g. multiple keyboards/mice for collaborative
|
||
editing)
|
||
- No multi-region isolation
|
||
|
||
**Why it's a problem**:
|
||
- Can't support multi-user scenarios (e.g. conference room with multiple
|
||
keyboards)
|
||
- No way to isolate apps between tenants
|
||
|
||
**What you should do instead**:
|
||
- For multi-tenant isolation: separate Python proxy processes per tenant,
|
||
containerized (Docker + bubblewrap)
|
||
- For multi-seat: implement `wl_seat` global per seat, route input
|
||
events by seat
|
||
|
||
#### K. Custom web-bitmapbuf protocol — apps must be Greenfield-aware
|
||
|
||
**What Greenfield does**:
|
||
- `protocol/web-bitmapbuf-unstable-v1.xml` is Greenfield's custom
|
||
extension
|
||
- Lets browser JS apps hand a GPU-backed `ImageBitmap` directly to the
|
||
compositor
|
||
- Standard Wayland apps (even browser-resident ones) fall to slow H.264
|
||
path
|
||
|
||
**Why it's a problem**:
|
||
- Apps must be rebuilt against `@gfld/client-protocol` to use the fast
|
||
path
|
||
- Standard Wayland apps can't take advantage of zero-copy GPU textures
|
||
- Browser-resident WASM apps are slow unless rebuilt
|
||
|
||
**What you should do instead**:
|
||
- Document clearly that the fast path requires Greenfield-aware apps
|
||
- For native Linux apps (your main use case), this isn't an issue — they
|
||
go through the H.264 path regardless
|
||
- For browser-resident WASM apps, encourage users to build against
|
||
`@gfld/client-protocol`
|
||
|
||
#### L. Connection loss handling
|
||
|
||
**What Greenfield does**:
|
||
- 10-minute zombie timeout on disconnect (`NativeAppContext.ts:152-160`)
|
||
- No state resumption on reconnect
|
||
- `eagerBufferContents` cache in `BufferStream.ts:31` is unbounded (TODO
|
||
at `:87` confirms the leak)
|
||
- App kill timers: SIGHUP@10min, SIGKILL@+10s
|
||
- Close code 1001 → immediate SIGHUP; 4001 → immediate SIGTERM
|
||
|
||
**Why it's a problem**:
|
||
- User loses all running apps on browser refresh (unless they reconnect
|
||
within 10 min)
|
||
- Memory leak from unbounded cache
|
||
- No way to survive proxy restart
|
||
|
||
**What you should do instead**:
|
||
- Bound the `eagerBufferContents` cache (e.g. max 100 frames)
|
||
- Persist app state on browser side (localStorage) so users can resume
|
||
- For proxy restart, checkpoint app state to disk (json file)
|
||
- Implement graceful reconnection with state sync (the KCP plumbing
|
||
exists)
|
||
|
||
#### M. No sandboxing
|
||
|
||
**What Greenfield does**:
|
||
- `spawn(executable, args, { env })` with no bubblewrap/seccomp/namespaces
|
||
- Basic Auth is a single replayable credential compared with `!==`
|
||
- `compositorSessionId` is client-generated
|
||
|
||
**Why it's a problem**:
|
||
- Native apps have full access to the host filesystem
|
||
- One compromised app can read all other apps' data
|
||
- Basic Auth credentials can be replayed
|
||
- Client-generated session IDs can be spoofed
|
||
|
||
**What you should do instead**:
|
||
- Sandbox every spawned app with bubblewrap + seccomp + per-session Linux
|
||
user namespace
|
||
- Replace Basic Auth with short-lived signed JWT bound to Odoo's session
|
||
model
|
||
- Server-generated session IDs (use `secrets.token_urlsafe(32)`)
|
||
|
||
#### N. Heavy toolchain
|
||
|
||
**What Greenfield does**:
|
||
- Yarn 4 + CMake + Ninja + Meson + pkg-config
|
||
- Two different Emscripten versions: 3.1.46 for FFmpeg, 3.1.60 for SDK
|
||
- SDK builds 30+ C libraries from source
|
||
- gtk4 WASM is currently broken
|
||
- Native build needs `autoconf libtool automake meson ninja-build bison
|
||
cmake build-essential gperf`
|
||
|
||
**Why it's a problem**:
|
||
- Complex build environment setup
|
||
- Long build times
|
||
- Emscripten version skew between libs and SDK
|
||
- gtk4 WASM broken means no GTK4 browser apps
|
||
|
||
**What you should do instead**:
|
||
- Stick with a single Emscripten version
|
||
- Pre-build WASM modules and ship them as artifacts (don't rebuild every
|
||
time)
|
||
- For Python, use `pip install` for all dependencies (simpler than CMake
|
||
+ Ninja)
|
||
- Skip the WASM app SDK entirely if you only care about native Linux apps
|
||
|
||
### Medium / low (but worth fixing)
|
||
|
||
#### O. WebCodecs usage
|
||
|
||
**What Greenfield does**:
|
||
- WebCodecs IS used (correcting the brief in some agents' notes)
|
||
- BUT HW-decode is disabled
|
||
- `VideoEncoder` is unused
|
||
|
||
**Why it's a problem**:
|
||
- Wastes GPU on software decode
|
||
- Could encode in browser (for upload to other peers) but doesn't
|
||
|
||
**What you should do instead**:
|
||
- Enable HW-decode by fixing the "frame offset is wrong" FIXME
|
||
- Consider WebCodecs `VideoEncoder` for any peer-to-peer video paths
|
||
|
||
#### P. WebCodecs hardware decode disabled
|
||
|
||
**What Greenfield does**:
|
||
- `Session.ts:93` has `// FIXME hardware decoding frame offset is wrong`
|
||
- `hardwareAcceleration: 'prefer-software'` is hardcoded
|
||
|
||
**Why it's a problem**:
|
||
- Software decode at 1080p60 is ~30% CPU on a modern laptop
|
||
- Battery life impact on mobile
|
||
- Wastes GPU
|
||
|
||
**What you should do instead**:
|
||
- Investigate and fix the frame offset bug
|
||
- Enable `prefer-hardware` for capable browsers
|
||
- Provide a runtime toggle
|
||
|
||
#### Q. Unbounded `eagerBufferContents` cache
|
||
|
||
**What Greenfield does**:
|
||
- `BufferStream.ts:31` has unbounded cache
|
||
- TODO at `:87` confirms the leak
|
||
|
||
**Why it's a problem**:
|
||
- Memory leak on long-running sessions
|
||
- Can OOM the browser tab
|
||
|
||
**What you should do instead**:
|
||
- Bound the cache to a maximum number of frames (e.g. 100)
|
||
- Use LRU eviction
|
||
- Periodic cleanup of orphaned entries
|
||
|
||
#### R. `H264NALDecoder.worker.ts` is minimal
|
||
|
||
**What Greenfield does**:
|
||
- 22 lines of code
|
||
- `@ts-ignore`'d
|
||
- No error boundary
|
||
|
||
**Why it's a problem**:
|
||
- Worker crashes silently
|
||
- No retry on transient errors
|
||
- No metrics on decode failures
|
||
|
||
**What you should do instead**:
|
||
- Add error boundary with retry logic
|
||
- Log decode failures to the proxy (for diagnostics)
|
||
- Pool workers for resilience
|
||
|
||
#### S. Magic undocumented WebSocket close codes
|
||
|
||
**What Greenfield does**:
|
||
- Close codes `4200`, `4201`, `4403`, `4001`, `1005` are used without
|
||
documentation
|
||
- Only codes 4000-4999 (application-level) tear down KCP
|
||
|
||
**Why it's a problem**:
|
||
- Hard to debug connection issues
|
||
- Close codes are magic numbers
|
||
|
||
**What you should do instead**:
|
||
- Document every close code in code comments
|
||
- Use named constants (e.g. `CLOSE_APP_TERMINATED = 4001`)
|
||
- Build a close-code → human-readable mapping
|
||
|
||
#### T. Generated protocol files committed
|
||
|
||
**What Greenfield does**:
|
||
- `packages/compositor-proxy/src/protocol/*.js` is generated but
|
||
committed to the repo
|
||
- Can drift from XML source
|
||
|
||
**Why it's a problem**:
|
||
- Easy to forget to regenerate
|
||
- PRs may include stale generated code
|
||
|
||
**What you should do instead**:
|
||
- Don't commit generated files
|
||
- Use `prebuild` script to generate them
|
||
- Add a CI check that regenerated files match committed ones
|
||
|
||
#### U. Confirmed copy-paste bug in `webcodec-buffer-decoder.ts`
|
||
|
||
**What Greenfield does**:
|
||
- `webcodec-buffer-decoder.ts:142-144, 156-157` has:
|
||
`this.opaqueDecoder = undefined` twice
|
||
- Should be `alphaDecoder` the second time
|
||
- Leaks the alpha decoder after errors
|
||
|
||
**Why it's a problem**:
|
||
- Memory leak
|
||
- Wasm decoder context not cleaned up properly
|
||
- Can cause decoder pool exhaustion
|
||
|
||
**What you should do instead**:
|
||
- Audit all `cleanup` paths in decoder code
|
||
- Add unit tests for error scenarios
|
||
|
||
## Design wins to copy
|
||
|
||
Not all of Greenfield's choices are bad. Here are 12 design wins worth
|
||
copying verbatim:
|
||
|
||
1. **Generator-driven protocol stubs** (`libs/compositor-generator/`,
|
||
`client-generator/`, `compositor-proxy-generator/`) — single XML source
|
||
for both sides. Eliminates wire-format drift.
|
||
2. **Dual-buffer commit semantics** (`Surface.ts`: `state` vs
|
||
`pendingState`, atomic merge with pixman damage regions). Classic
|
||
Wayland correctness.
|
||
3. **UserShellApi decoupling** — shell sees only `{events, actions}`,
|
||
never Wayland resources. This is THE integration seam you'll use.
|
||
4. **Asynchronous browser compositor** — per-client channels, never blocks
|
||
on a slow client. Essential for multi-app scenarios.
|
||
5. **KCP-over-WebSocket** for UDP-like latency over TCP (`kcp.ts`, 1170
|
||
lines). Better than plain TCP for real-time video.
|
||
6. **WebCodecs primary + WASM fallback + capability probe** (`Session.ts`).
|
||
Modern browsers get the fast path, older ones still work.
|
||
7. **Zero-copy dmabuf → encoder** via standard `linux-dmabuf-unstable-v1`
|
||
(`westfield-dmabuf.c`). Pixels stay on GPU.
|
||
8. **Per-surface encoder feedback channel** (`EncoderFeedback.ts`)
|
||
measuring client decode duration — even though adaptation isn't wired
|
||
up yet, the telemetry is right.
|
||
9. **`ReconnectingWebSocket`** with exponential backoff + message queue.
|
||
Survives transient network drops.
|
||
10. **Minimal shell** — 562 lines of TypeScript total in `compositor-shell`;
|
||
kiosk variant is 40 lines. Demonstrates that the shell can be tiny.
|
||
11. **Honest docs** — `docs/pages/design/` openly admits WebSocket/TCP is
|
||
wrong for real-time and waits on WebTransport. Useful as a reference
|
||
for what trade-offs were considered.
|
||
12. **AGPL-3.0** — deliberate copyleft, but be aware before integrating.
|
||
If you want to keep your modifications proprietary, fork the MIT
|
||
libs and reimplement the AGPL parts.
|
||
|
||
## Top-5 recommendations for the user's Odoo 20 + JS/Python compositor
|
||
|
||
1. **Skip native Node addons entirely.** Use `pywayland` (cffi),
|
||
`python-xkbcommon`, and GStreamer's `gi.repository.Gst` Python
|
||
bindings. One Python process can replace the entire
|
||
`compositor-proxy-cli`. See `10-porting-to-odoo-js-python.md` for
|
||
details.
|
||
|
||
2. **Sandbox every spawned app** with bubblewrap + seccomp + per-session
|
||
Linux user namespace; replace Basic Auth with short-lived signed JWT
|
||
bound to Odoo's session model.
|
||
|
||
3. **Render in a Web Worker with `OffscreenCanvas`** from day one; ship
|
||
`EncodedVideoChunk` / `ArrayBuffer` transfers (zero-copy) to the
|
||
worker; use `VideoFrame` directly as a WebGL texture source — no
|
||
`copyTo`.
|
||
|
||
4. **Build an adaptive multi-codec encoding router** (PNG for ≤64×64 or
|
||
static, VP8/H.264 for mid-size, H.264/AV1 for video), driven by the
|
||
`EncoderFeedback` measurement Greenfield already produces but doesn't
|
||
consume.
|
||
|
||
5. **Version your wire protocol from commit #1** — prefix every binary
|
||
message with `u16 protocolVersion`; document every close code; use
|
||
JSON `{"v":1, "type":"..."}` for the control plane.
|
||
|
||
## License implications of AGPL-3.0
|
||
|
||
The four `packages/*` (compositor, compositor-proxy, compositor-proxy-cli,
|
||
compositor-shell) are **AGPL-3.0-or-later**. Implications:
|
||
|
||
- If you host Greenfield's `compositor-proxy` as a network service and
|
||
modify it, you MUST release your modifications under AGPL-3.0+.
|
||
- The browser-side `@gfld/compositor` is also AGPL. If you modify it
|
||
(e.g. to integrate with Odoo), your modifications must be AGPL.
|
||
- The lower-level `libs/*` (common, client-protocol, compositor-protocol,
|
||
the generators, compositor-wasm, compositor-ffmpeg-h264, xtsb) are MIT
|
||
or Apache-2.0 — these are safe to use without copyleft obligations.
|
||
- **Strategy to avoid AGPL**: use the MIT libs as-is, reimplement the
|
||
AGPL parts from scratch (browser compositor + native proxy). The
|
||
Python port described in `10-porting-to-odoo-js-python.md` is a clean
|
||
way to do this — if you write your Python proxy from scratch, you can
|
||
license it however you want.
|
||
|
||
## What Greenfield's codebase reveals about its engineering process
|
||
|
||
The codebase reveals several engineering process insights:
|
||
|
||
1. **One-person show** — most commits are by Erik De Rijcke. The codebase
|
||
has the strengths (consistent vision, tight integration) and weaknesses
|
||
(single point of failure, no code review diversity) of a one-person
|
||
project.
|
||
2. **Long-running project** — code references back to 2017-era commits
|
||
(WebRTC was a previous transport; the `kcp.ts` MTU comment says
|
||
"webrtc datachannel MTU"). The architecture has evolved through several
|
||
major pivots.
|
||
3. **rc1 is meaningful** — the maintainer has been working toward 1.0 for
|
||
years and rc1 represents a deliberate freeze. Don't expect 1.0 final
|
||
any time soon, but also don't expect major architecture changes.
|
||
4. **Honest about limitations** — docs/pages/design/ explicitly admits
|
||
what's wrong and what's planned. Use this to inform your own roadmap.
|