Skip to content

feat(custom-app): inline compose transport on start (Feature 4 — LLD §4.2) - #147

Merged
omarsy merged 1 commit into
mainfrom
feat/custom-inline-transport
Oct 23, 2026
Merged

omarsy merged 1 commit into
mainfrom
feat/custom-inline-transport

Conversation

@omarsy

@omarsy omarsy commented Oct 23, 2026

Copy link
Copy Markdown
Member

Custom-app-deploy foundation Feature 4, transport slice (LLD §4.2; epic row: "Greffer consumes inline compose" — preflight + handshake halves already merged as #146).

What

  • GreffonStartRequest.compose_yaml: str | None — additive; bounded to the same 1 MiB order as the preflight request (an unbounded body is tenant-triggerable memory before any render budget runs).
  • When present, get_compose_file_from_repository skips the URL fetch and parses the inline bytes. repository_url becomes conditionally required: exactly one compose source per start (_require_compose_source model validator — neither → the same 422 the old required field produced; both → refused rather than silently preferring one). The min_length=1 floor stays for the catalog path.
  • The containment Feature 3 trust discriminator (_compose_strict) — wired in feat(containment): allowlist compose keys; namespace all mounts; stage baked files #142 with "activates with the inline transport" — is now live: inline bytes get the strict allowlist set.
  • Unparsable inline bytes → InlineComposeError → clean 422 before any port allocation or volume write. Those bytes passed the identical parse in /preflight/, so divergence means the manager sent something else — name the transport, never a 500.
  • Version 0.13.0: the manager stamps min_greffer_version on custom versions and the gate must name a release that actually understands the field (0.9.x–0.12.x would pass a 0.9.0 floor but extra=ignore the bytes).

Wire compatibility

  • Old manager → new greffer: omits compose_yaml → None → the URL path, byte-for-byte today's behavior.
  • New manager → old greffer: old model ignores compose_yaml, 422s on the missing repository_url — prevented by the version gate, which is the HLD's stated answer ("the old-node problem is handled by min_greffer_version").

Tests

Model (conditional source, empty/oversized inline, empty URL floor), repository (inline skips the fetch, invalid YAML raises, absent inline still fetches), routes (inline start 200 with strict=True asserted through to get_greffon_info, invalid inline 422 with nothing started).

Suite: 1106 passed; the only failure is the pre-existing-on-main oracle/catalog-pin environment artifact (local catalog checkout differs from the pinned 5652a75). The load-fragile workers_register test deselected locally per usual.

Follow-up slices under the same Feature 4 row: all-tier disk admission (HLD §9) and the one-shot-status mechanism (Story 5 — the HLD names it an open design gap).

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-23T18:21:16.856407Z ab7eac8 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ab7eac889e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/utils/greffon/repository.py Outdated
inline = greffon.get('compose_yaml')
if inline:
try:
return yaml.safe_load(inline)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Detach YAML-aliased service definitions before mutation

For a newly supported inline compose where two services reference the same YAML-anchored service mapping, yaml.safe_load() returns the same dictionary object for both services. The later normalization and create_compose_template_from_greffon() pipeline mutates service definitions in place; when it clears and rebuilds ports, volumes, and networks, both services therefore share the rebuilt lists and receive duplicate or cross-service entries. Such an input passes preflight but /start/ can emit a compose different from the validated source and let the asynchronous compose child fail after a successful response. Reject shared service mappings or clone each service definition independently before passing the parsed inline compose into the mutating pipeline.

Useful? React with 👍 / 👎.

…LD §4.2)

GreffonStartRequest grows compose_yaml (str | None, bounded to the same
1 MiB order as the preflight request): when present, the pinned compose
bytes travel in the start request and the URL fetch is skipped — a
custom version has no URL to fetch. repository_url becomes conditionally
required (exactly one compose source per start: neither is the same 400
the old required field produced; both is refused rather than silently
preferring one). The containment Feature 3 trust discriminator
(_compose_strict) is live on this path: inline bytes are unreviewed
input and get the strict allowlist set. Version 0.13.0 — the release
the manager's min_greffer_version floor must name (0.9.x-0.12.x pass a
0.9.0 floor but extra=ignore the bytes).

Adversarial-review round folded:
- P0 alias-DAG: a sub-KiB anchor DAG under an allowed key (e.g.
  environment) passes every shape check AND returned a green preflight
  (its own walk is id-deduped), but /start/'s render accounting walks
  the tree in the parent process with no dedup — 2^25 visits measured
  at ~71s, ~2^35 possible, pinning the single worker. Fix at the
  boundary: compose_shape.parse_compose_yaml (SafeLoader subclass
  refusing AliasEvent — anchor definitions without references stay
  legal) used by BOTH tenant parse sites (start inline branch and
  preflight). The allowlist permits no x- extension fields, so the
  anchor-and-merge pattern was never deployable; refusing the
  reference names it at preflight instead of a green light that pins a
  node. The preflight's dedup walker keeps its direct unit test as
  defense in depth.
- P2 RecursionError (deeply nested inline YAML) is not a YAMLError —
  escaped both handlers and 500'd; now mapped to the same clean 422 at
  both sites.
- P3 InlineComposeError bound at import time in the controller (the
  file's own convention — an except on a mocked module attribute
  TypeErrors mid-handler); the both/neither comment says 400 (the
  request-validation handler), not 422.

Suite: 1112 passed; the only failure is the pre-existing-on-main
oracle/catalog-pin environment artifact.
@omarsy
omarsy force-pushed the feat/custom-inline-transport branch from ab7eac8 to 93dd48b Compare October 23, 2026 18:52
@omarsy
omarsy merged commit ba1e46a into main Oct 23, 2026
1 check failed
@omarsy
omarsy deleted the feat/custom-inline-transport branch October 23, 2026 18:56
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