#Neko Obfuscator Agent Instructions
- Do not implement or optimize for a special sample, benchmark, class, method, owner, descriptor, crash site, log string, or known test artifact.
- All modifications must follow JVM ABI, All JVM features must be supported.
- Every fix must be generic and architecture-level.
- Runtime, obfuscation, native, ABI, performance, and GC behavior changes must be backed by a complete, concrete evidence chain before implementation. The evidence must identify the exact failing invariant, runtime path, instruction/site, generated artifact, log, or test observation that proves the root cause and justifies the change. Do not modify those paths based on speculation, intuition, probability language, or phrases such as "possibly", "likely", "probably", "high probability", "I think", or "I suspect". If the evidence is incomplete, gather more evidence first.
- Documentation, agent-instruction, formatting, and other non-runtime changes require a clear stated reason and diff review, but do not require runtime artifacts unless they change executable behavior.
- Do not jump to a later-stage workaround while earlier prerequisites are open. For recorded high-risk plans, update the todo first with a plan-level dependency reason if the order must change.
- Do not mark stale work complete. A stale jar, stale generated C file, compile-only result, unit-only result, or previous run is not proof.
- After a verified completed high-risk substep that has a recorded
.plan/entry, commit the implementation and the todo checkbox update before continuing. - Do not revert or overwrite unrelated user work. Work with existing changes if they affect the task.
- Use the repository/system
./gradlewdirectly. If the active harness runs in a sandbox or approval environment, use that harness's escalation mechanism to run./gradlewrather than asking the user conversationally for permission. Do not create, copy, or use a temporarygradlew.
Instruction precedence: system and developer instructions from the active agent harness take priority over this repository file. When this file names a tool that is unavailable in the current harness, use the nearest equivalent mechanism and state the substitution in the final report.
Use the lightest workflow that still preserves auditability:
- Routine tasks: documentation edits, typo fixes, formatting, small scoped
refactors, and other non-runtime changes do not require a repository
.plan/document unless the user asks for one. Use the active agent todo system only when the harness requires it for multi-step work. - High-risk tasks: JVM obfuscation semantics, native translation/runtime,
JNI removal, hidden-key propagation, CFF, ABI compatibility, reflection or
MethodHandle rewriting, GC compatibility, performance gates, and any change
requiring runtime/code validation must be split into concrete subtasks before
implementation starts. Record those subtasks both in the active agent todo
system and in a matching
.plan/todo document. The.plan/document itself must include the baseline/evidence-gathering work, the written plan/review work, and the implementation repair work; the first implementation-stage task must be the concrete repair being made, not a later workaround or validation cleanup. - User-requested planning: if the user explicitly asks for a plan, audit trail, staged execution, or checkpoint commits, follow the high-risk planning workflow even for otherwise routine work.
Plan decomposition, subagent review, and atomic commit discipline are mandatory for every recorded plan:
- A plan must be split into multiple concrete tasks, and every task must be split into multiple concrete subtasks unless the task is indivisible by its stated acceptance criteria. Record the dependency order before implementation, including baseline capture and plan-writing/review as explicit plan-body entries before the first implementation subtask.
- After a plan is written and before any implementation starts, dispatch a subagent plan-intake review to verify that the plan is credible, complete, evidence-backed, correctly decomposed, and compliant with this file. If the plan-intake review fails, revise the plan and repeat the review before starting implementation.
- After the plan document and plan-intake review are complete, but before the plan commit and before any implementation, report the plan to the user. The report must summarize the planned change scope, why this change order and approach are being used, and the concrete benefits and drawbacks/tradeoffs of the plan.
- After the plan document and plan-intake review are complete and the plan has been reported to the user, commit only the plan/todo files before starting any implementation. This is the required checkpoint for the approved plan itself.
- Each subtask must have a bounded scope, evidence requirement, validation target, and completion criteria. Do not merge unrelated concerns into one subtask merely to reduce commit count.
- After each subtask is implemented and validated, dispatch a subagent review before committing. The review must analyze whether the current diff, evidence, validation, and scope discipline satisfy this file's standards.
- If the subagent review passes, commit only that subtask's implementation and matching todo or plan update before starting the next subtask. If the review fails, record the reason, continue iterating on the same subtask, rerun the required validation, and dispatch another review before commit.
- When an entire plan is completed, dispatch a separate subagent plan review to audit task/subtask completion, evidence freshness, validation coverage, commit atomicity, and compliance with this file before final acceptance.
- For technical details that require substantial external research, or for complex tasks where independent investigation can reduce risk, dispatch subagents to research or complete bounded parts of the work and reconcile their findings against repository evidence before implementation.
- For multi-file work whose files or topics are independent, dispatch subagents in parallel to research or complete non-overlapping parts when the harness supports it. Parallel subagents must not edit the same files or depend on uncommitted changes from each other without an explicit handoff.
- When a subagent has completed its assigned task and no later task needs to resume that same subagent context, close that subagent to preserve context budget.
- Never use one subagent to complete two independent tasks. Independent tasks require separate subagent contexts so findings, assumptions, and evidence do not become mixed across task boundaries.
Each recorded high-risk subtask must include its scope, required evidence, validation command or runtime target, and completion criteria. Do not start implementation for a recorded high-risk subtask until those fields are recorded.
When one recorded high-risk subtask is completed and freshly validated, immediately update both todo locations and create a git commit containing only that subtask's implementation plus its matching todo update. This commit is the required checkpoint and rollback point before starting the next recorded high-risk subtask.
For routine tasks, keep commits focused and avoid committing unrelated user work. If unrelated changes are already present, commit only the files changed for the current task or ask before committing.
If any implementation detail, required behavior, acceptance condition, task boundary, or user intent is unclear, do not infer or invent the missing details. Use the ask tool to ask the user for the missing information before editing code or configuration. If the ask tool is unavailable, ask a concise direct question in the conversation and wait for the answer.
- No JNI fallback, soft fallback, skip-on-error path, or original-bytecode fallback.
- No JVMTI.
- No new JVM runtime helper layer outside the planned native/bootstrap surface.
- No new Java-side bootstrap or new
<clinit>behavior beyond the existing native-loader entry. JNI_OnLoadmust stay minimal:GetEnv, capture anchors, initialize native metadata, then stop using the JNI function table.- Outside that minimal
JNI_OnLoadpath, generated/runtime native code must not useNEKO_JNI_FN_PTR,(*env)->,env->,Call*Method,Get*Field,FindClass,NewStringUTF,NewObject*,Throw*, JNI monitor calls, JNI array calls, or equivalent JNI wrappers. - Missing VMStruct, JVM symbol, class, method, field, string metadata, GC barrier, CodeHeap support, or call stub support must hard abort/error. It must not fall back to JNI, skip the method, or keep original bytecode.
- ZGC/Shenandoah compatibility must not be achieved by skipping classes or methods. Missing required barriers must abort.
- JVM obfuscation fixes must preserve or increase obfuscation strength. Do not disable, bypass, downgrade, skip, or special-case any transform to make a jar pass.
- Control-flow flattening must use correct dynamic dispatch. State transitions must be computed from live control-flow state and must not collapse into static dispatch, self-canceling predicates, dead stores, or constant-only switch selectors.
- Control-flow flattening, invokedynamic obfuscation, method-parameter obfuscation, constant obfuscation, and string obfuscation must remain compatible when all are enabled together.
- Hidden method keys and control-flow keys must stay semantically linked. Key material passed between transformed methods must match the target method's actual ABI, descriptor, dispatch family, and runtime entry path.
- Full-obfuscation coverage is mandatory for original application classes: enabled transforms must cover every eligible original class, method, member, constant, string, reflective datum, and call path.
- True JVM ABI surfaces that must retain JVM-required shape are exempt from name
or descriptor obfuscation only to the extent required by the ABI, including
class initializers (
<clinit>),main([Ljava/lang/String;)V, and inherited external API override slots. Constructors (<init>) may retain only the JVM- mandated name and return shape; original application constructors must still receive parameter rewriting, hidden-key injection, packed-argument carrier changes, and protected callsite rewriting wherever their descriptor is not an inherited or external ABI contract. This ABI exemption must not disable method-parameter obfuscation, control-flow parameter changes, hidden keys, packedObject[]carriers, or protected callsite rewriting for non-ABI original application paths. - Hidden
longkey parameters and packedObject[]parameter carriers are mandatory obfuscation surfaces. Do not remove, bypass, downgrade, or exclude them for reflection, loaders, annotations, MethodHandle, LambdaMetafactory, virtual/interface dispatch, benchmarks, tests, or compatibility fixes. Reflection and dynamic invocation compatibility must be achieved by generic rewriting of lookup names, lookup descriptors, argument arrays, resource paths, metadata, and generated-member filters to the final obfuscated ABI. Do not preserve original application descriptors or names as a workaround, except for the true JVM ABI surfaces listed above. - Reflection, MethodHandle, loader, annotation, and dynamic-invocation compatibility rewrites are not allowed to expose plaintext class/member names, descriptor strings, resource paths, parameter-type constants, seed material, or invoke adapters. Any inserted bytecode that represents user-visible reflective data must remain eligible for CFF, constant obfuscation, string obfuscation, and invokedynamic obfuscation, or must emit an equivalent protected form directly. Reflection lookup names, descriptor arrays, annotation element calls, resource paths, and rewritten reflective invocation arguments must not remain as plain strings or plain constants after compatibility rewriting. Generated markers may protect only transform bookkeeping that would be invalid to reprocess; they must not be used to skip obfuscation of reflective application data.
- Reflection loading, reflective invocation, declared-member lookup
(
getDeclared*/get*), MethodHandle lookup/invocation, LambdaMetafactory, and invokedynamic-created call paths are mandatory full-obfuscation surfaces for original application code. They must be made compatible by rewriting lookup names, descriptors, parameter type arrays, argument arrays, bootstrap arguments, handles, callsites, and injected hidden parameters to the final obfuscated ABI; they must not be left unobfuscated, skipped, or preserved as original descriptors/names for compatibility. - Injected helper classes and helper members may be obfuscated as needed for correctness, security, and compatibility, but they do not require mandatory full-obfuscation coverage. Injected outliner code is not a helper exemption: outliners must receive full obfuscation coverage.
- Dynamic key propagation is mandatory. Do not replace dynamic key transfer with
static
stateKey, static seed constants, descriptor-only recomputation, or any equivalent uncorrelated key source. - Do not expose raw static keys in generated bytecode. If a canonical target key must be captured for a delayed, escaping, reflective, or lambda call path, it must be dynamically encoded or decoded from live obfuscated state before use.
- LambdaMetafactory, MethodHandle, reflection, virtual/interface dispatch,
abstract-method dispatch, packed
Object[]parameters, constructor paths, exception paths, loops, recursion, and asynchronous execution must preserve key correctness without fallback. - Original application constructor paths are mandatory full-obfuscation surfaces.
Every object creation sequence, including
new/dup/invokespecial <init>, reflective construction, MethodHandle construction, serialization-like construction paths, and invokedynamic/lambda construction paths, must rewrite the creation callsite and injected constructor parameters to match the final obfuscated constructor ABI. Constructors must not be excluded from parameter changes or hidden-key propagation merely because the JVM name is<init>. - InvokeDynamic obfuscation must not introduce Java bridge methods, adapter methods, synthetic runtime dispatch layers, or helper classes to compensate for key-transfer bugs.
- New Java helpers are forbidden unless they are part of the existing planned transform surface. String decode helpers are allowed only for string obfuscation and must not be used as invokedynamic or CFF bridges.
- Do not add fallback behavior, original-call fallback, original-bytecode fallback, skip-on-error, try-original-on-failure, or reduced-obfuscation rescue paths for JVM transforms.
- Do not implement fixes for a specific jar, class, method, stack trace, log string, benchmark, or test artifact. JVM transform fixes must be generic and architecture-level.
- A generated jar is not considered fixed until the freshly generated artifact runs successfully with the same enabled transform set that exposed the bug.
- Never reduce, approximate, thin, segment, merge, coarsen, batch, delay, or otherwise weaken control-flow flattening coverage or block granularity to make a jar pass. Do not change CFF block construction, CFF block boundaries, CFF block count, or CFF block selection as a size or verifier workaround.
- Do not expose, persist, log, encode as plain constants, or otherwise leak any flow key, method key, state key, key-transfer material, or derived dispatch key in generated bytecode, helper APIs, metadata, maps, logs, resources, or synthetic members.
- Do not implement key self-canceling logic. Key updates and key transfer must not contain mathematically removable pairs, neutral operations, inverse pairs, static folds, constant-only recomputation, dead-key stores, or expressions that can be algebraically simplified into a static key or a descriptor-only value.
- All key propagation must be nonlinear and driven by live external key input. A method entry key must drive every CFF block's active state, every future key update, and every key transfer to downstream calls or delayed execution paths.
- Method entry keys must remain semantically live from method entry through all CFF dispatcher decisions, transition updates, hidden-method key propagation, invokedynamic sites, reflection paths, lambda/MethodHandle paths, exception paths, loops, recursion, and asynchronous execution. Recomputing equivalent values from static seeds, state constants, descriptors, owner/name strings, or block indexes is not an acceptable replacement for live key flow.
- Any architectural size fix must preserve full-strength CFF and dynamic key propagation. It may move equivalent protected logic only if the moved path is still keyed by live method-entry material and does not introduce helper-layer fallback, static-key exposure, reduced transform coverage, or changed CFF block semantics.
Before editing code for a new high-risk runtime subtask, record the runtime target row that will prove the changed path. Then:
- Make one coherent generic change.
- Regenerate the affected native artifact with repository
./gradlew, using the harness escalation mechanism directly when sandbox approval is required. - Run the required runtime targets from the todo.
- Inspect stdout, stderr, native logs,
hs_errfiles, and generated C when claiming JNI removal. - If anything fails, fix and rerun. If it passes, commit only the implementation and the matching todo checkbox update.
Routine documentation or agent-instruction-only changes are validated by diff review and do not require Gradle or runtime target execution.
A recorded high-risk runtime subtask remains [ ] or [-] until the changed
path is exercised by a freshly regenerated runtime artifact. Every source
change after a pass invalidates the previous proof and requires a new runtime
run.
Runtime validation must reject:
- SIGSEGV, SIGABRT, verifier errors, or VM fatal errors.
translated=0.Native compilation produced no libraries.- skip-on-error behavior.
- missing-symbol fallback.
- JNI fallback.
- original bytecode fallback.
Resolver and bind-time subtasks must prove that the current bind/runtime path uses the resolved pointer, offset, or entry. Hot-path opcode subtasks must prove the runtime jar executes the opcode through the new native path and generated C contains no forbidden JNI wrappers. Cleanup/removal subtasks must prove the removed fallback is no longer needed and missing capability paths hard abort.
R-build: cleanly regenerate native artifacts with the repo Gradle wrapper.R-test: run the generated TEST native jar.R-obfusjack: run the obfusjack Java fixture.R-native-obfusjack: run the generated obfusjack native jar.R-native-test: run the generated TEST native jar under the native path.R-inspect: inspect generated C/native output for forbidden JNI and fallback markers.R-negative: prove missing required symbols/capabilities fail closed instead of falling back.
R-build.R-testrepeated 5 times.R-obfusjackrepeated 5 times.R-native-test.R-inspect.
Median thresholds:
- TEST Calc <= 20 ms, must be same or less than baseline(before edit) jar.
- obfusjack matrix-mul Seq <= 14 ms, must be same or less than baseline(before edit) jar.
- Platform <= 44 ms, must be same or less than baseline(before edit) jar.
- Virtual <= 35 ms.
- Parallel and VThreads must preserve the prior wins required by the todo.
If a threshold fails, make a new generic optimization commit and rerun the gate. Do not special-case the benchmark.
GC strict compatibility requires TEST and obfusjack runs under:
- G1.
- Parallel.
- Serial.
- ZGC with
ZVerifyViews. - Shenandoah with verification enabled.
Final native-gentle-flamingo completion requires:
- Static grep showing only the allowed
GetEnv. - Dynamic validation showing no JNI calls on the translated path.
- Thread state stable as
_thread_in_java, except controlled NJX transitions. - TEST Calc <= 20 ms.
- obfusjack full green.
- GC compatibility without skip behavior.
The following are explicitly out of scope and must not be introduced:
- JNI fallback.
- JVMTI.
- JVM helper classes outside the existing planned transform surface.
- Calc-only shipping proof.
- ZGC/Shenandoah skip behavior.
- Never create any files/folders in /tmp folder.