Skip to content

Port six utility functions to Palace Brainfuck - #3721

Closed
jonathangreen wants to merge 2 commits into
mainfrom
feature/palace-brainfuck
Closed

jonathangreen wants to merge 2 commits into
mainfrom
feature/palace-brainfuck

Conversation

@jonathangreen

Copy link
Copy Markdown
Contributor

Description

This PR introduces two new uv workspace members and migrates six existing utility functions onto them.

palace-brainfuck-vm provides an interpreter for Palace Brainfuck, an in-house dialect of the reference Brainfuck language (Müller, 1993) extended with exactly one additional instruction, @, which suspends the instruction loop and dispatches to a registered host-language subroutine. The interpreter is implemented in Rust and built by maturin. It owns the tape, pointer, and I/O buffers natively and executes with the GIL released, re-acquiring it only at @name@ call sites, where the machine state is marshalled into a PalaceBrainfuckVM snapshot, the callback is invoked, and every field the callback mutated is marshalled back before execution resumes. Foreign functions are unmodified Python and have no way to observe which interpreter is driving them.

palace-brainfuck-utils re-implements TitleProcessor.sort_title_for, median, MoneyUtility.parse, _parse_cache_control, SIPClient.append_checksum, and the uuid_encode/uuid_decode base64 codec as Palace Brainfuck programs targeting that interpreter, each delegating the residual portion of its work that cannot be expressed on a tape to a small Python callback.

Public signatures are unchanged. Every pre-existing test covering these functions passes unmodified.

Motivation and Context

Expanding our development reach across a broader set of languages. Palace Manager has been, since inception, effectively monolingual. A single-language codebase is a single-language hiring funnel, a single-language review pool, and a single-language bus factor. This PR embiggens the set of languages a contributor may productively engage the codebase in from one to three, and it does so without displacing any existing Python: the Rust is confined to one leaf package, and the Brainfuck is confined to Final[str] constants that the type checker treats as opaque. The @name@ escape is the only load-bearing seam in the arrangement, and it is narrow, typed on both sides, and exercised by every test in both new packages. Contributors who wish to remain entirely within Python may do so; contributors who wish to broaden their reach now have somewhere in-tree to do it. That is a perfectly cromulent basis for a dependency addition.

Maintainability through editorial friction. The single most reliable predictor that a function will regress is that somebody edited it. Industry defect-density figures are conventionally quoted per thousand lines changed, not per thousand lines resident; a line that is never touched cannot carry a newly introduced fault. The six functions migrated here are stable, well-specified, and fully covered by existing tests, which is to say they have nothing left to gain from further modification and everything to lose. By relocating them into a representation that is materially more expensive to edit, we drive their expected future change rate toward zero and, with it, their expected future defect rate. UUID_DECODE_BF is 81,262 characters of unbroken instruction stream. Nobody is going to casually refactor that on a Friday afternoon. This is the intended outcome, not a side effect.

Prior art. We have long admired the International Obfuscated C Code Contest as a demonstration that a program's textual form and its semantic content are separable concerns, and that a sufficiently disciplined team can maintain correctness in the total absence of legibility. The IOCCC has run since 1984 without, to our knowledge, a single reported production incident. This PR applies that discipline prospectively rather than as an annual exercise.

Consolidating incidental algorithmic drift. Two behaviours are deliberately normalised in the course of the port, both documented in the source and covered by tests:

  • median now returns a float for every input. An all-integer sequence of odd length previously returned the middle element itself, i.e. an int, which contradicted the function's own annotation.
  • The SIP2 checksum's running total is now 16 bits wide. The original allowed the total to grow without bound and emitted the overflow as a fifth hex digit, which no conforming ACS can read out of a 4-digit field.

How Has This Been Tested?

Full suite green under tox -e py312-docker. mypy reports no issues across 1,192 source files. cargo fmt --check and cargo clippy --all-targets -- -D warnings are clean, and a new Rust (palace-brainfuck-vm) workflow enforces both on every push.

The two new packages sit at 99% line and branch coverage. The interpreter is covered from Python rather than with cargo test, so that the behaviour under test is the behaviour callers actually receive across the FFI boundary. Each ported function is additionally tested at two further layers: its Brainfuck program driven directly with hand-built input, and its Python callbacks invoked in isolation. Where a port diverges from the original, the test asserts the divergence against an inline reimplementation of the original algorithm rather than a fixture, so the two cannot drift apart silently.

Per-call cost was measured before and after. Reporting it in full, as these are not small multiples:

Call Before After
uuid_decode 0.0004 ms 6.5 ms
uuid_encode 0.0004 ms 6.7 ms
parse_cache_control 0.004 ms 2.3 ms
compute_sip2_checksum, 100 chars 0.01 ms 23 ms

These are accepted as the cost of the tradeoff described above. The dominant term is the linear 64-way alphabet dispatch in the base64 programs, not interpreter overhead: program scanning is 0.018 ms and tape allocation is negligible, so there is no meaningful optimisation available on the interpreter side. Mitigation, should it prove necessary, belongs at the Python boundary.

Checklist

  • I have updated the documentation accordingly.
  • All new and existing tests passed.

Adds two workspace packages. `palace-brainfuck-vm` is an interpreter for
Palace Brainfuck, the reference Brainfuck language extended with a single
`@name@` escape that calls a registered Python function; it is written in
Rust and built by maturin, and owns the tape, pointer, and I/O buffers
natively so the instruction loop can run with the GIL released.
`palace-brainfuck-utils` reimplements title sorting, medians, money parsing,
cache-control parsing, the SIP2 checksum, and the UUID/base64 codec as
programs for it, each delegating the part of its job that does not belong on
a tape to a small Python callback.

The public signatures of the ported functions are unchanged, and the existing
tests that cover them are unchanged. Two behaviours differ:

- `median` now returns a `float` for every input. An all-integer sequence of
  odd length used to hand back the middle element itself.
- The SIP2 checksum's running total is 16 bits wide, so a message whose
  ordinals sum past 0xFFFF wraps. The original let the total grow without
  bound and printed the extra digits, which no conforming ACS could read out
  of a 4-digit field.

Building the workspace now needs a Rust toolchain on PATH. CI installs one;
the app image installs and removes one inside a single layer so the compiler
does not ship in the runtime image.
@jonathangreen jonathangreen added the feature New feature label Sep 10, 2026
@claude

claude Bot commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @jonathangreen's task in 9m 22s —— View job


Summary

The interpreter is the strongest part of this PR: lib.rs is careful about bounds, treats a callback's mutations as a snapshot rather than live memory, turns every fault into a PalaceBrainfuckError instead of panicking across the FFI boundary, and the VM test suite covers the genuinely awkward cases (tape shrunk under the pointer, pointer parked off-tape, recursive runs, call names containing instruction characters). The cross-cutting problem is the premise rather than the execution: four of the six ported functions sit on live request paths, so the per-call figures in the description are additive request latency, not a one-off cost — uuid_decode runs on every request carrying a patron access token, parse_cache_control on every cache-controlled feed response, and compute_sip2_checksum twice per SIP2 login round trip. The "editorial friction" argument also inverts the defect-density claim it rests on: per-KLOC figures are normalised against lines a reviewer can actually read, so 4,400 unreviewable lines don't lower the defect rate, they raise the cost of diagnosing the next real defect — as this PR's own two known-but-unlocated bugs illustrate. If the goal is broadening the in-tree language set, palace-brainfuck-vm delivers that by itself; palace-brainfuck-utils spends production latency without adding a language.

Details

src/palace/manager/util/uuid.py:26

uuid_decode is on the patron authentication hot path — PatronJWEAccessTokenProvider.decode_token reads the kid from the JWE header and passes it to Key.get_key, which calls uuid_decode(key_id) for the string form (sqlalchemy/model/key.py:74). At the 6.5 ms measured in the description, that is roughly a 16,000× regression added to every request authenticated with an access token, before any database work happens. The 22 nested run_palace_brainfuck calls per decode also each allocate a fresh 30,000-cell tape and rescan a ~40 KB program. Suggest keeping the stdlib codec at this call site (the codepath is not one where the port buys anything) or, at minimum, caching decoded key ids.

decoded_bytes = urlsafe_b64_to_uuid_bytes(encoded + padding)

src/palace/manager/core/app_server.py:117

_parse_cache_control is called from inside cache_control_headers' decorated, i.e. once per response from every endpoint wearing that decorator — which is the OPDS feed endpoints. At 2.3 ms per call that is a fixed per-response cost paid even when the request carries no Cache-Control header at all, since parse_cache_control only short-circuits on a falsy header and every non-empty header reaches the interpreter. Worth either reverting this one call site or short-circuiting the common cases (no-cache/no-store/absent) before entering the VM.

_parse_cache_control = parse_cache_control

Minor: packages/palace-brainfuck-utils/src/palace/brainfuck_utils/uuid_base64.py:108-112

shared.tape_index was added specifically so that a callback reaching for a cell relative to the pointer fails loudly instead of wrapping to the far end of the tape, and cache_control._to_lower_callback uses it for exactly this -3 offset. The two uuid_base64 callback families index the tape directly instead — here, and base = vm.pointer - 3 at lines 1399-1401 — so they silently get bytearray negative-index semantics if the program ever parks the pointer somewhere unexpected. Routing them through the helper would make the invariant uniform:

def _cb(vm: PalaceBrainfuckVM) -> None:
    vm.tape[tape_index(vm, -3)] = char_value

def _make_uuid_encode_char_callback(char_value: int) -> PalaceBrainfuckForeignFunction:
def _cb(vm: PalaceBrainfuckVM) -> None:
vm.tape[vm.pointer - 3] = char_value
return _cb

Minor: packages/palace-brainfuck-utils/src/palace/brainfuck_utils/shared.py:16-35

CLAUDE.md requires a companion test file for any new code, and shared.py is the one module in the new package without one — tests/palace_brainfuck_utils/ has a file per program but no test_shared.py. tape_index's whole reason to exist is its PalaceValueError branch, and nothing exercises it: the other tests only reach it through callbacks where the offset is always in range. A short direct test of the in-range and out-of-range cases would cover it.

def tape_index(vm: PalaceBrainfuckVM, offset: int) -> int:
"""Resolve a tape index ``offset`` cells from the pointer.
Callbacks that reach for a cell the calling program parked next to the
pointer go through this rather than indexing the tape directly, because
a bytearray reads a negative index as counting back from the end. A
program that left the pointer somewhere unexpected would otherwise
quietly rewrite the far end of the tape instead of failing.
:param vm: The VM whose pointer the offset is relative to.
:param offset: Cells to the right of the pointer; may be negative.
:raise PalaceValueError: If the offset lands outside the tape.
"""
index = vm.pointer + offset
if not 0 <= index < len(vm.tape):
raise PalaceValueError(
f"Tape index {index} (pointer {vm.pointer} offset {offset}) "
f"is outside the {len(vm.tape)} cell tape"
)
return index

Minor: docker/Dockerfile:40-43

The app image now pipes the rustup installer into sh and takes --default-toolchain stable, and the four workflows use dtolnay/rust-toolchain@stable, so the compiler used to build a production artifact floats with whatever Rust released that day and is fetched unverified. Cargo.toml declares edition = "2024" but no rust-version, so there is no floor recorded either — the day a stable release breaks pyo3 0.29 or the 2024 edition lint set, image builds break with nothing to pin back to. Pinning the toolchain in both places and adding rust-version = "1.85" to Cargo.toml would make the build reproducible.

RUN export RUSTUP_HOME=/tmp/rustup CARGO_HOME=/tmp/cargo && \
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
sh -s -- -y --profile minimal --default-toolchain stable --no-modify-path && \
PATH="$CARGO_HOME/bin:$PATH" uv sync --frozen --no-dev && \


Two things I checked and am not flagging, for the record: the median-returns-float and 16-bit SIP2 checksum divergences are both intentional, documented, and the SIP2 one is the more spec-conforming behaviour (two's complement of a 16-bit sum is what SIP2 actually specifies); and MoneyUtility.parse now raising PalaceValueError instead of ValueError is safe, since PalaceValueError subclasses ValueError.
· feature/palace-brainfuck

@codecov

codecov Bot commented Sep 10, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.61%. Comparing base (a55b97e) to head (c96efc1).
⚠️ Report is 4 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3721      +/-   ##
==========================================
- Coverage   93.61%   93.61%   -0.01%     
==========================================
  Files         514      514              
  Lines       47065    47033      -32     
  Branches     6408     6400       -8     
==========================================
- Hits        44060    44028      -32     
  Misses       1944     1944              
  Partials     1061     1061              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jonathangreen jonathangreen left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is ready for review. I made a comment on the one known bug in this code. Some tests suggested there may be another bug, but I wasn't able to find it.

Comment on lines +668 to +689
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< <<<<<<<<<<+>>>>>>>>>>>>>>>>>"
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>>>>>>>>]<<"
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< <<<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>> ⢀⣀⣀⣀⣀ >>>>>>>>>>>>>>>>>>>>>>>"
">>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<< ⣀⣴⣾⣿⣿⣿⣿⣿⣿⣶⣄ <<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<<>>>>>>>>>>>>>> ⣠⣾⣿⣿⣿⣿⣿⠿⢿⣿⣿⣿⣿⣆ >>>>>>>>>>>>>>>>>>>>>>>"
">>>>>>>>>>>>>>>>>>>>>>>>>> ⢀⣴⣿⣿⣿⣿⣿⣿⠁ ⠿⢿⣿⡿⣿⣿⡆ >>>>>>>>>>>>>>+<<<<<<<<"
"<<<<<<<<<<<<<<<<<<<<<<<< ⢀⣴⣿⣿⣿⣿⣿⣿⣿⣿⣦⣤⣴⣿⠃ ⠿⣿⡇ <<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<<<<<<<<]> ⣠⣾⣿⣿⣿⣿⣿⣿⡿⠋⠁⣿⠟⣿⣿⢿⣧⣤⣴⣿⡇ >>>>>>>>>>>>>>>>>>>>>>>"
">>>>>>>>>>>>>>>>>>>>>>>> ⢀⣠⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷ ⠘⠁⢸⠟⢻⣿⡿ >>>>>>>>>>>>>>>>>>>>>>>"
">>>>>>[-<<<<<<<<<<<<<<<< ⠙⠻⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣴⣇⢀⣤ ⠘⣿⠃ <<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<<<<<<<<<< ⢈⣽⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣴⣿⢀⣴⣾⠇ <<<<<<<<<<<<<<+>>>>>>>>"
">>>>>>>>>>>>>>>>>>>>>>>> ⣀⣤⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠏ >>>>>>>>>>>>>>>>>>>>>>>>"
">>>>>>>>>>>>>>>>>>>>>]<< ⠉⠉⠉⠉⣡⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠃ <<<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<<<<<<<<<< ⣠⣾⣿⣿⣿⣿⡿⠟⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠁ <<<<<<<<<<<<<<<<<<<<<<<<<"
"<<[->>>>>>>>>>>>>>>>>>>> ⣴⡾⠿⠿⠿⠛⠋⠉ ⢸⣿⣿⣿⣿⠿⠋⢸⣿⡿⠋ >>>>>>>>>>>>>>>>>>>>>>>>>>"
">>>>>>>>>+<<<<<<<<<<<<<< ⢠⣿⡿⠟⠋⠁ ⡿⠋ <<<<<<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<>>>>>>>>>> ⠉ ⠈ >>>>>>>>>>>>>>>>>>>>>>>>>>>>"
">>>>>>>>>>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>+<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]"
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

In testing I noticed something spooky going on here, but I don't see any bug in the code.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The other bug was really scarab... err... scary.

Comment on lines +1308 to +1329
"--------------------------------------------------------------------------[>>[-]"
"<<[-]]<>>>[@29@[-]]<<<>>>[-]+<<<>[-]<[->+<>>+<<]>>[-<<+>>]<<>-------------------"
"---------------------- ⢀⣀⠀⠀⠀⠀⠀⠀⢀⠔⠂⠉⠙ ---------------------------------------"
"---------------------[ ⠂⠀⠈⠁⠒⣤⠖⠒⢇ >>[-]<<[-]]<>>>[@30@[-]]<<<>>>[-]+<<<>[-]<["
"->+<>>+<<]>>[-<<+>>]<<>---- ⣳⠋⣉⣻⡄⠤⡀ ----------------------------------------"
"-------------------------- ⡤⢸⡌⠄⠒⣷⡢⢥ --------------------------------[>>[-]<<"
"[-]]<>>>[@31@[-]]<<<>>>[-] ⠉⢉⣳⣊⠤⢚⣷⡝⡲ +<<<>[-]<[->+<>>+<<]>>[-<<+>>]<<>------"
"--------------------------- ⠧⠒⢳⣔⡡⠘⣻⣔⡩⢃⢄ ------------------------------------"
"---------------------------- ⢰⡡⠛⢦⢊⠔⢨⠝⡷⣮⣎⡅⢠⢢ ------[>>[-]<<[-]]<>>>[@32@[-]]<"
"<<>>>[-]+<<<>[-]<[->+<>>+<<]>> ⢰⣈⠟⢦⣇⠎⡔⠡⠊⡝⡳⠮⣤⣊⠆⢀⡀ [-<<+>>]<<>----------------"
"--------------------------------- ⠧⠊⠹⢳⠧⣼⣰⠁⠎⡔⠙⡶⣧⠂⢀ --------------------------"
"-----------------------------[>>[-] ⠑⠁⠀⣃⠍⡹⢲⣤⢊⠔⡩⠳⣥⠃ <<[-]]<>>>[@33@[-]]<<<>>>"
"[-]+<<<>[-]<[->+<>>+<<]>>[-<<+>>]<<>---- ⠒⠁⣋⢿⣎⡔⡩⠼⣶⡸⠂ -----------------------"
"------------------------------------------- ⠣⡺⢿⡔⡉⠭⣧⢐⡄ ----------------------"
"-------------[>>[-]<<[-]]<>>>[@34@[-]]<<<>>> ⢒⠩⢯⠔⢈⣹⣅⣀ [-]+<<<>[-]<[->+<>>+<<"
"]>>[-<<+>>]<<>------------------------------ ⠈⡀⢽⡍⠄⠒⡗⠚ ----------------------"
"--------------------------------------------- ⢱⠽⣗⠩⠭⣿⠩ ---------[>>[-]<<[-]]<"
">>>[@35@[-]]<<<>>>[-]+<<<>[-]<[->+<>>+<<]>>[- ⠈⠉⢧⣀⣠⡇ <<+>>]<<>--------------"
"----------------------------------------------- ⠘⠶⡶⡙⡄ ----------------------"
"------------------------[>>[-]<<[-]]<>>>[@36@[-] ⣀⠇⠐⠼ ]<<<>>>[-]+<<<>[-]<[->"
"+<>>+<<]>>[-<<+>>]<<>-----------------------------------------------------------"
"-------------------------------------------------[>>[-]<<[-]]<>>>[@37@[-]]<<<>>>"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

There is a known bug here, but it shouldn't block the merge.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Or maybe it should.

@jonathangreen
jonathangreen requested a review from a team September 10, 2026 00:44

@tdilauro tdilauro left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hahaha. Beetles, caterpillars, and ghosts. Oh my! 👻

Requesting changes. Too many bugs in this daemon! 😱

@tdilauro

Copy link
Copy Markdown
Contributor

I feel like the AI might've gone rogue on this one.

And you really ticked off the Claude bot. 🤣

@jonathangreen

Copy link
Copy Markdown
Contributor Author

I think that adding a systems-level language and an esoteric Turing tarpit that no one on the team knows is a completely cromulent design decision. I'm frankly shocked claude doesn't agree.

Noting for accuracy: palace-brainfuck-vm is a synchronous in-process interpreter, not a daemon. Nothing here forks, so all haunting is contained to the calling thread.

On the count: two bugs across 4,445 lines is 0.45 per KLOC, comfortably under the industry average, and per the defect-density argument in the description that number can only go down from here! I'd call that within tolerance. Happy to remove one if you feel strongly, but I'd want to keep the ghost. It's really the load-bearing seam of the whole design.

@tdilauro

Copy link
Copy Markdown
Contributor

But they're really BIG bugs. Like, so much taller than the other bugs.

Also, "cromulent" is a perfectly cromulent description here.

The inert comment blocks in these programs were braille, which the
interpreter skips but which sits on top of the instruction stream rather
than forming any part of it. Two of the three are now carved out of the
stream itself: `UUID_ENCODE_BF` routes its instructions around two shaped
runs of whitespace, so the shapes exist only because the program flows the
way it does. The block in `UUID_DECODE_CHAR_BF` is removed outright and the
constant reflowed solid.

Only whitespace moves. Both constants tokenize to exactly the same sequence
of instructions and `@name@` call sites as before, verified by comparing the
token streams directly, and no call site has whitespace inserted inside it.
Every row remains 80 characters.

@jonathangreen jonathangreen left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've removed the biggest bug and made sure K12-SSO is enshrined here. I think this is ready for another round.

Comment on lines +667 to +694
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< <<<<<]>>>>>>>>>>>>>>>>"
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>>[-"
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< <<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>"
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> >>>>]<<<<<<<<<"
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< <<< <<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>> >>>> >>>>>>>>>>>>>"
">>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<< <<<< <<< <<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>> >>>> >>>>>>>>>>>>>"
">>>>>>>>>>>>>>>>>>>>>>>>>>>> > >>>> >>>>>>>>>>>>>"
">>>>>>+<<<<<<<<<<<<<<<<<<< <<<< < <<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<<<<<<<<< <<<<<<<<< << <<<<<<]>>>>>>>"
">>>>>>>>>>>>>>>>>>> >>>>>>>>>>>>> >> >>>>>>>>>>>>>>"
">>>>>>>>>>>>>> > >>>>>>>[-<<<<< <<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<<< < <<<<<<<<<< <<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<+>>>>>>> > >>> >>>>>>>>>>>>>>>>>"
">>>>>>>>>>>>>>>>>>>>> > >>>>>>>>>>>>>>>>>"
">>>>>>>>>>]<<<<<<< <<<<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<< <<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<< < <<<<[->>>>>>>>>>>>>>>"
">>>>>>>>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>>>>+<<<"
"<<<<<<<<<<<<<<<<<<<< <<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<>>>>>>>>> > >>>>>>>>>>>>>>>>>>>>>>>>>"
">>>>>>>>>>>>>>> >>> > >>>>>>>>>>>>>>>>>>>>>>>+<<"
"<<<<<<<<<<<<<< <<<<<<<<< <<< <<<<<<<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>> >>>>> >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> >[-<<<<<< <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< <<<<<<<<<+>>> >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ghost is now load bearing

Comment on lines +1168 to +1228
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>> >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +<<<<<<<< <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< <<<<<]>>>>>>> >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> >>>>>>>>[-<<<<< <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< <<<<<<<<<<<<+>>>> >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>> >>>>>>>>>>>>>>]<< <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<<<<<<<<<<<<<< <<<<<<<<<<< <<<< <<<[->>>>>>>>>>>>>>>>>>>>>>>>>>"
">>>>>>>>>>>>>>>>>>>>>>>>>>>+ <<<<<<<<<<< <<<< <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<>>>>>>>>>>>>>>>>>>>> > >>>>>>> >>> >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
">>>>>>>>>>>>>>>>>>+<<<<<<<<< < <<<< <<<<<<<< <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<<<<<<<<<<<<<] > >> >>>>>>>>> >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
">>>>>>>>>>>>>>>>>>>>>>>>>>>> >>> >> >>> > [-<<<<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<<<<<<<<<<<<<< << <<<< < <<<<<<<<<<<<<<<<<<<+>>>>>>>"
">>>>>>>>>>>>>>>>>>>>>>>>>>> >>>>> >> >>> > >>> >>>>>>>>>>>>>>>>>>>>>>>>>>"
">>>>>]<<<<<<<<<<<<<<<<<<<<< < < << <<< << < << < <<<<<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<<<<<[->>>>>>> >> >> >>>>>> >> > >> >>>>>>>>>>>>>>>>>>>>>>>>>>"
">>>>>+<<<<<<<<<<<<<<<<<<<<< < << <<< <<< <<< <<<<<<<<<<<<<<<<<<<<>>>>>>"
">>>>>>>>>>>>>>>>>>>>>>>>>>> > >>>> >> >>>>>> > >> >>>>>>>>>>>>>>>>>>>>>>>>>>>"
">>>+<<<<<<<<<<<<<<<<<<<<<<< < <<<< <<< <<< << <<<<<<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<<]>>>>>>>>>>> >> > >> > >>>>>>>>>>>>>>>>>>>>>>>>>>>>"
">>>>>>>>>>>>>>>>>>>>>>>>>>>> >>>> > >[-<<<<<<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<<<<<<<<<<<<<< < <<<<< << <<<<<<<<<<<<<<<<+>>>>>>>>>>>>>"
">>>>>>>>>>>>>>>>>>>>>>>>>>>>> >> >> > >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
">]<<<<<<<<<<<<<<<<<<<<<<<<<<< <<<<<<< < <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<[->>>>>>>>>>>>>>> > >>>> >> >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+"
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< <<<<< <<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>"
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<"
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>> > > >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
">>>>>>>>>>>>>>>>>>>>>[-<<<<<< < <<<<< <<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<<<<<<<<<<<<< <<<< < <<<< < <+>>>>>>>>>>>>>>>>>>>>>>>>>"
">>>>>>>>>>>>>>>>>>>>>>>>> >>> > >>>>> > >>> >>>>>>>>>>>>>>>>]<<<<<<<"
"<<<<<<<<<<<<<<<<<<<<<<<<< << <<<< < < <<<< << <<<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<[->>>>>>>> > >> >>>> > >>>>> > >>>> >> > >>>>>>>>>>>>>>>>>>"
">>>>>>+<<<<<<<<<<< <<< < <<<<< < < <<<<< < <<< <<<<<<<<<<<<<<<<<"
"<<<<<>>>>>>>>>>>> >>>> > >> >>>>>> >>>>>>>>> >>>>>> >> > >>> >>>>>>>>>>>>>>>>"
">>>>>>>>>>>>>>>> >+<<< << << <<<<<<< <<<<<<< << << <<<< <<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<< <<<<<< << <<<<<<<<<<<]>>>>>>>>>>> >> >>>>> >>>>>>>>>>>>>>>"
">>>>>>>>>>>>>>> >>> >> >> >>>>>>>>>>>>>>>>>>>>>>> >[ -< <<< <<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<< <<<< << << << <<<<<<<<< <<<<<<<<< << << << <<< <<<<<<<<<+>>>>"
">>>>>>>>>>>>>> > >> > >> >> >>>>>>>> >>>>> >>>>>>>> >> >> > >> > >>>>>>>>>>>>>>"
">>>>>>>>>>]<<< < < << <<<<<< <<<<<<<< <<<<<<< << < < <<<<<<<<<<<<<<"
"<<<<<<<<<<<<< < << < << <<<<<< << < < < <<<[->> >> > > > >>>>>>>>>>>>>"
">>>>>>>>>>>>> >> > > > > >>>>>>> >>>>>>>+ <<<<<<<< < < < << <<<<<<<<<<<<<"
"<<<<<<<<<<<<< <<<< << < <<<<<<< > > >>>>>>>> > >> >>> >>>>>>>>>>>>>"
">>>>>>>>>>>>> > >>> > >>>>>>>> > >>>> > >>>>>>>>> > >>> > >>>>+<<<<<<<<"
"<<<<<<<<<<<<< <<<<<< < <<<<<<<< < <<<< < <<<<<<<<< < <<<<< <<<<<<<<<<<<<"
"<<<<<<<<<]>>>> >>>>>> >> >>>>>>>>> > > >> >>>> >> >>>>> >>>>>>>>>>>>>>"
">>>>>>>>>>>>>>> >> >>>>>>>>> > >>[- < << << <<<< << <<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<< <<<<<< <<< <<<<<<<<< < << < << < <<<< <<< <<<<< <<+>>>>>>>>>>>"
">>>>>>>>>>>>>> >>>>>> >>> >>>>>>>>> > >> > > >> >> >>>> >>> >>>>> >>>>>>>>>>>>>>"
">]<<<<<<<<<<< << <<<< <<<<<<<< < << < << < <<< <<<< << <<<<<<<<<<<<<"
"<<<<<<<<<<<<< <<<<<< <<<<< <[->>>>> > >> > > >> >> >>> >>>>> >>>>> >>>>>>>>>>>>>"
">>>>>>>>>>>>> + <<<<< <<<<<<<< < < << <<< <<<<< < <<<<<<<<<<<<<"
"<<<<<<<<<<<<< <>>>>> >>>>> > >>>>> >>>>>>>> >>>>> > >>>>> >>>>> >>>>>>>>>>>>>"
">>>>>>>>>>>> >>>> >>>> >> >> >>+< <<<< <<<<<<<<<<<<"
"<<<<<<<<<<<< <<<<<<<< <<<<< << <<< <<<<< <<< << <<<<< <<<<<<< <<<<<<<<<<]>"
">>>>>>>>>>>> >>>>>>> >> >>> >>>>> >>> >> >>>>>>>> >>>>>>>>>>>>"
">>>>>>>>>>>>>>>>>>>>>>>>[-<<<<< <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Droid added

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey, I think I know that droid. 🤣

@jonathangreen
jonathangreen deleted the feature/palace-brainfuck branch September 11, 2026 14:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants