Skip to main content

Prompt Library

Purpose: Provide ready-made prompts developers can copy into coding assistants for Octant v2 work. Audience: Developers using coding assistants for Octant v2 strategy development. Level: Beginner Source of truth: The prompts reference the pinned versions and verification commands from the workflow pages. Use this page when: starting a coding assistant session for any Octant v2 task. Do not use this page for: understanding the Octant v2 architecture -- use the concept pages instead.

Each prompt below is self-contained. Copy the fenced code block into your coding assistant (Claude, Cursor, Copilot, etc.) and it will have everything it needs to complete the task.


1. Set up the starter repo
TASK: Set up the Octant v2 strategy starter template at its target snapshot.

STEPS:
1. Clone the starter template and check out the pinned commit:
git clone https://github.com/golemfoundation/octant-v2-strategy-foundry-mix.git my-strategy
cd my-strategy
git checkout ddd405c18bb0c765c0256ca952d4d9f4034cf3ec

2. Install dependencies:
yarn

3. Initialize and pin the core submodule:
git submodule update --init --recursive
git -C dependencies/octant-v2-core checkout 36ed6ad6665661a18f83394d561fa75c68ccf4ac

4. Install the remaining Solidity dependencies:
forge soldeer install
# If this panics with "Attempted to create a NULL object", stop here and
# report the toolchain failure. Do NOT continue to forge build.

5. Apply the required _symbol compatibility fixes before the first build.
The pinned starter template targets an older BaseStrategy / ITokenizedStrategy.initialize() shape.
Against octant-v2-core@36ed6ad6665661a18f83394d561fa75c68ccf4ac, raw "forge build" will fail until these four edits are applied:

a. src/strategies/yieldDonating/YieldDonatingStrategy.sol
- Add "string memory _symbol" after "_name" in the constructor.
- Pass "_symbol" through to the BaseStrategy constructor immediately after "_name".

b. src/test/yieldDonating/YieldDonatingSetup.sol
- Add "YDS" as the _symbol constructor argument in the new Strategy(...) call, immediately after "YieldDonating Strategy".

c. src/strategies/yieldDonating/YieldDonatingStrategyFactory.sol
- Add "string calldata _symbol" after "_name" in newStrategy(...).
- Pass "_symbol" through to the YieldDonatingStrategy constructor immediately after "_name".

d. src/test/yieldDonating/YieldDonatingFunctionSignature.t.sol
- Update the encoded initialize signature from:
"initialize(address,string,address,address,address,address,bool)"
to:
"initialize(address,string,string,address,address,address,address,bool)"
- Add "SYM" as the symbol argument in the encoded calldata.

6. Verify compilation:
forge build

EDIT BOUNDARIES:
- You may edit: foundry.toml, .env, src/strategies/yieldDonating/YieldDonatingStrategy.sol, src/strategies/yieldDonating/YieldDonatingStrategyFactory.sol, src/test/yieldDonating/YieldDonatingSetup.sol, src/test/yieldDonating/YieldDonatingFunctionSignature.t.sol
- Do NOT modify anything under dependencies/octant-v2-core/

VERIFICATION:
- Apply the four _symbol compatibility fixes before running "forge build".
- "forge build" must complete with no errors after those fixes are applied.
- If forge build fails with lint errors from the core submodule, add this as a top-level section in foundry.toml:
[lint]
lint_on_build = false
Then re-run "forge build".

IMPORTANT:
- The starter template commit is ddd405c18bb0c765c0256ca952d4d9f4034cf3ec
- The octant-v2-core commit is 36ed6ad6665661a18f83394d561fa75c68ccf4ac (tag 1.2.0-develop.15)
- If a later "git submodule update --init --recursive" resets the core pin, re-pin the core submodule to 36ed6ad6665661a18f83394d561fa75c68ccf4ac. The reconcile-starter-pin.sh helper ships with the docs bundle (not the starter repo), so download it first from /09_agent_assisted_development/scripts/reconcile-starter-pin.sh and run it from the starter repo root.
- Report exact error messages if anything fails. Do not guess fixes.
- See anti-patterns: /docs/developers/agent_assisted_development/agent-anti-patterns
2. Implement Hello World Strategy
TASK: Build a minimal sDAI Yield Donating Strategy following the Hello World tutorial.

CONTEXT:
- Start repo: octant-v2-strategy-foundry-mix at commit ddd405c18bb0c765c0256ca952d4d9f4034cf3ec
- Core pinned to: octant-v2-core at commit 36ed6ad6665661a18f83394d561fa75c68ccf4ac (tag 1.2.0-develop.15)
- Shared YDS implementation address: 0xE8797A98710518A6973Cc8612f98154EECF2C711

PREREQUISITE — COMPATIBILITY FIXES (apply before forge build):
The starter template uses an older constructor shape missing the _symbol parameter.
Apply these four fixes FIRST:

1. src/strategies/yieldDonating/YieldDonatingStrategy.sol — add "string memory _symbol" parameter to the constructor, between _name and _management, and pass it through to BaseStrategy.

2. src/test/yieldDonating/YieldDonatingSetup.sol — add "YDS" as the _symbol argument in the constructor call, after the strategy name.

3. src/strategies/yieldDonating/YieldDonatingStrategyFactory.sol — add "string calldata _symbol" parameter to newStrategy() and pass it through to the YieldDonatingStrategy constructor.

4. src/test/yieldDonating/YieldDonatingFunctionSignature.t.sol — update the initialize() ABI signature from
"initialize(address,string,address,address,address,address,bool)"
to
"initialize(address,string,string,address,address,address,address,bool)"
and add "SYM" as the symbol argument.

After fixes, run: forge build. Do not treat the starter scaffold's broad make test target as the compatibility gate; it needs concrete TEST_ASSET_ADDRESS and TEST_YIELD_SOURCE values and may require separate test reconciliation.

STRATEGY IMPLEMENTATION:
Create src/strategies/yieldDonating/SDAIStrategy.sol — a strategy that:
- Inherits BaseHealthCheck
- Accepts DAI deposits and deploys them into sDAI (0x83F20F44975D03b1b09e64809B757c47f942BEeA)
- Implements _deployFunds, _freeFunds, _emergencyWithdraw, _harvestAndReport
- Overrides availableDepositLimit and availableWithdrawLimit
- Validates sDAI.asset() == DAI in the constructor
- Uses exact per-deposit forceApprove for the sDAI allowance, requires shares > 0, and clears allowance back to zero
- Reports deployed value with previewRedeem(sDAIShares), not convertToAssets

YDS constructor parameters (as of commit 36ed6ad6665661a18f83394d561fa75c68ccf4ac):
_asset (address), _name (string memory), _symbol (string memory),
_management (address), _keeper (address), _emergencyAdmin (address),
_donationAddress (address), _enableBurning (bool), _tokenizedStrategyAddress (address)

TEST:
Create test/SDAIStrategy.t.sol — a fork test that:
- Uses vm.createSelectFork with ETH_RPC_URL
- Deploys the strategy with the shared implementation at 0xE8797A98710518A6973Cc8612f98154EECF2C711
- Casts to ITokenizedStrategy for ERC-4626 calls (delegatecall pattern)
- Tests deposit, report (simulated profit via deal()), and redemption
- For the first empty YDS deposit, asserts user shares are depositAmount - 1_000 and address(0xdead) holds 1_000 locked shares
- Sets health-check limits: setProfitLimitRatio(500) and setLossLimitRatio(100)

DEPLOY SCRIPT:
Create script/Deploy.s.sol that reads PRIVATE_KEY, MANAGEMENT, KEEPER, EMERGENCY_ADMIN, DONATION_ADDRESS from .env.

EDIT BOUNDARIES:
- You may edit/create files under: src/strategies/, test/, script/, .env, foundry.toml
- Do NOT modify anything under dependencies/octant-v2-core/

VERIFICATION:
1. forge build
2. ETH_RPC_URL=<your mainnet archive RPC> forge test --match-test test_depositAndReport -vv
3. In a second terminal: anvil --fork-url $ETH_RPC_URL
4. forge script script/Deploy.s.sol --rpc-url http://127.0.0.1:8545 --broadcast

IMPORTANT:
- Do not use address(0) for any role parameter — the initializer will revert.
- Do not invent contract addresses — use only those listed above.
- Never use "--broadcast" against a mainnet RPC during the tutorial. Broadcast only to the local Anvil fork at http://127.0.0.1:8545.
- Report exact error messages if anything fails. Do not guess fixes.
- See anti-patterns: /docs/developers/agent_assisted_development/agent-anti-patterns
3. Write a new Yield Donating Strategy
TASK: Implement a custom Yield Donating Strategy (YDS) from the starter template.

CONTEXT:
- Start repo: octant-v2-strategy-foundry-mix at commit ddd405c18bb0c765c0256ca952d4d9f4034cf3ec
- Core pinned to: octant-v2-core at commit 36ed6ad6665661a18f83394d561fa75c68ccf4ac (tag 1.2.0-develop.15)
- Shared YDS implementation address: 0xE8797A98710518A6973Cc8612f98154EECF2C711

YDS CONSTRUCTOR SIGNATURE (BaseHealthCheck, as of commit 36ed6ad6665661a18f83394d561fa75c68ccf4ac):
_asset (address) — the underlying ERC-20 token
_name (string memory) — strategy name for ERC-4626 shares
_symbol (string memory) — strategy symbol for ERC-4626 shares
_management (address) — can adjust strategy parameters post-deploy
_keeper (address) — can call report() and tend()
_emergencyAdmin (address) — can shutdown and emergency-withdraw
_donationAddress (address) — receives 100% of reported profit as shares
_enableBurning (bool) — burn donation shares first during loss
_tokenizedStrategyAddress (address) — shared implementation for delegatecall (use 0xE8797A98710518A6973Cc8612f98154EECF2C711)

REQUIRED HOOKS TO IMPLEMENT:
- _deployFunds(uint256 amount) — move idle assets into the yield source
- _freeFunds(uint256 amount) — withdraw assets from the yield source
- _harvestAndReport() returns (uint256 totalAssets) — return idle + deployed value
- _emergencyWithdraw(uint256 amount) — emergency recovery after shutdown

RECOMMENDED OVERRIDES:
- availableDepositLimit(address) — mirror upstream deposit limits
- availableWithdrawLimit(address) — mirror upstream withdraw limits

IMPORTANT PATTERNS:
- Use forceApprove (not plain approve) for ERC-20 allowances, approve only the current deploy amount, and clear allowance back to zero after the external call
- Validate the yield source asset matches the strategy asset in the constructor
- For ERC-4626 wrappers, report deployed value with previewRedeem(strategyWrapperShares)
- Use BaseHealthCheck and set realistic profitLimitRatio / lossLimitRatio
- Cast strategy to ITokenizedStrategy for ERC-4626 and report() calls in tests
- Account for YDS minimum liquidity in tests: the first empty-strategy deposit mints assets - 1_000 user shares and locks 1_000 shares at address(0xdead)
- When disabling burn protection after it has been enabled, prefer reportAndDisableBurning() if pending dragon shares or an unreported loss may exist

PREREQUISITE:
Apply the four _symbol compatibility fixes to the starter template before forge build.
(See prompt #2 for the exact fixes.)

EDIT BOUNDARIES:
- You may edit/create files under: src/strategies/, test/, script/, .env, foundry.toml
- Do NOT modify anything under dependencies/octant-v2-core/

VERIFICATION:
1. forge build
2. forge test --fork-url $ETH_RPC_URL -vv

IMPORTANT:
- Do not use address(0) for any role parameter.
- Do not invent contract addresses.
- If you later add deployment verification, use a local Anvil fork at http://127.0.0.1:8545 for any "--broadcast" commands during the tutorial path.
- Report exact error messages if anything fails. Do not guess fixes.
- See anti-patterns: /docs/developers/agent_assisted_development/agent-anti-patterns
4. Audit a custom strategy
TASK: Review a custom Octant v2 strategy contract against conventions and anti-patterns.

CONTEXT:
- Core pinned to: octant-v2-core at commit 36ed6ad6665661a18f83394d561fa75c68ccf4ac (tag 1.2.0-develop.15)
- Shared YDS implementation address: 0xE8797A98710518A6973Cc8612f98154EECF2C711
- Shared YSS implementation address: 0xFe064acA6acFF4eFbE496271A665F0a9D66d6da1

CHECKLIST — verify each item:

1. CONSTRUCTOR SIGNATURE:
- For YDS strategies: inherits BaseHealthCheck, constructor passes parameters to BaseHealthCheck in this order:
_asset, _name, _symbol, _management, _keeper, _emergencyAdmin, _donationAddress, _enableBurning, _tokenizedStrategyAddress
- For YSS strategies: check the YSS-specific constructor pattern with the shared implementation at 0xFe064acA6acFF4eFbE496271A665F0a9D66d6da1
- Confirm _symbol parameter is present (required as of 1.2.0-develop.15)

2. SHARED IMPLEMENTATION ADDRESS:
- YDS must use 0xE8797A98710518A6973Cc8612f98154EECF2C711
- YSS must use 0xFe064acA6acFF4eFbE496271A665F0a9D66d6da1
- The address must NOT be a placeholder like 0xCAFE or address(0) — BaseStrategy delegate-calls during construction and will break

3. NO DIRECT VAULT DEPLOYMENT:
- Strategy should NOT deploy a MultistrategyVault directly
- MultistrategyVault must be deployed through MultistrategyVaultFactory

4. ROLE PARAMETERS:
- No role parameter (management, keeper, emergencyAdmin, donationAddress) should be address(0)
- The initializer will revert on zero addresses

5. HOOK IMPLEMENTATION:
- _deployFunds, _freeFunds, _harvestAndReport are implemented
- _emergencyWithdraw is implemented (not just a no-op)
- availableDepositLimit and availableWithdrawLimit are overridden (not left as permissive defaults)

6. APPROVAL PATTERN:
- Uses forceApprove (from SafeERC20), not plain approve
- Approval is set for the exact deploy amount inside _deployFunds
- Allowance is cleared back to zero after the external deposit/supply call
- No standing type(uint256).max approval is left on an external vault or pool

7. AAVE-SPECIFIC CHECKS, IF APPLICABLE:
- Do not cache PoolDataProvider as an immutable; resolve it from addressesProvider at runtime
- Include RewardsController wiring if claimAaveRewards is expected
- availableDepositLimit returns 0 when the reserve is paused, inactive, or frozen
- availableWithdrawLimit still returns idle assets when the reserve is paused or inactive
- Supply-cap headroom accounts for accruedToTreasuryScaled via getReserveNormalizedIncome

8. HEALTH-CHECK CONFIGURATION:
- setProfitLimitRatio and setLossLimitRatio are called during deployment
- Default lossLimitRatio is 0 (any loss reverts report()), which is usually wrong for production

9. YDS 1.2.0-develop.15 ACCOUNTING:
- First empty-strategy deposits account for the 1,000-share minimum-liquidity lock at address(0xdead)
- Tests expect apiVersion() == "1.1.0", not "1.0.0"
- Direct setEnableBurning(false) is not assumed to succeed while pending dragon shares or unreported loss exists; use reportAndDisableBurning() where appropriate

10. .env CONFIGURATION:
- ETH_RPC_URL is set
- PRIVATE_KEY, MANAGEMENT, KEEPER, EMERGENCY_ADMIN, DONATION_ADDRESS are set
- None of the address values are zero

11. CORE SUBMODULE:
- dependencies/octant-v2-core is at commit 36ed6ad6665661a18f83394d561fa75c68ccf4ac
- No files under dependencies/octant-v2-core/ have been modified

EDIT BOUNDARIES:
- Do NOT modify anything under dependencies/octant-v2-core/

IMPORTANT:
- Report each finding with the exact file path and line number.
- Do not guess fixes — describe the problem and reference the relevant convention.
- See anti-patterns: /docs/developers/agent_assisted_development/agent-anti-patterns
5. Deploy or configure a PaymentSplitter
TASK: Deploy a PaymentSplitter instance through the canonical factory for fixed-share yield routing.

CONTEXT:
- Core pinned to: octant-v2-core at commit 36ed6ad6665661a18f83394d561fa75c68ccf4ac (tag 1.2.0-develop.15)
- PaymentSplitterFactory address: 0x5711765E0756B45224fc1FdA1B41ab344682bBcb

IMPORTANT RULES:
- Do NOT deploy PaymentSplitter directly — always use PaymentSplitterFactory
- The split is FIXED at initialization and cannot be changed later
- PaymentSplitter uses a pull-payment model — recipients must call release() to claim

FACTORY METHODS (pick the one that fits your use case):

1. createPaymentSplitter(address[] payees, string[] payeeNames, uint256[] shares_)
— Basic deployment, no ETH, non-deterministic address

2. createPaymentSplitterWithETH(address[] payees, string[] payeeNames, uint256[] shares_)
— Forwards attached ETH to the new splitter

3. createPaymentSplitterWithSalt(address[] payees, string[] payeeNames, uint256[] shares_, bytes32 salt)
— Deterministic CREATE2 address

4. createPaymentSplitterWithETHAndSalt(address[] payees, string[] payeeNames, uint256[] shares_, bytes32 salt)
— Both deterministic address and ETH forwarding

EXAMPLE DEPLOYMENT:
```solidity
address splitter = PaymentSplitterFactory(0x5711765E0756B45224fc1FdA1B41ab344682bBcb)
.createPaymentSplitter(
payees, // address[] — recipient addresses
payeeNames, // string[] — human-readable labels
shares // uint256[] — proportional share units (e.g. [70, 20, 10])
);
```

EDIT BOUNDARIES:
- You may edit: deployment scripts, .env, configuration files
- Do NOT modify: src/core/PaymentSplitter.sol, src/factories/PaymentSplitterFactory.sol, anything under dependencies/octant-v2-core/

VERIFICATION:
After deployment, call these on the returned splitter address:
1. payee(0), payee(1), ... — confirm each recipient address matches your inputs
2. shares(payeeAddress) for each recipient — confirm share units match your configuration
3. Route a small test amount to the splitter, then call release(token, payeeAddress) for one recipient — confirm the correct proportional amount is transferred

TOKEN COMPATIBILITY:
- PaymentSplitter assumes standard ERC-20 transfer semantics
- Do NOT use with rebasing tokens or fee-on-transfer tokens

IMPORTANT:
- Do not invent factory addresses — use 0x5711765E0756B45224fc1FdA1B41ab344682bBcb
- Report exact error messages if anything fails. Do not guess fixes.
- See anti-patterns: /docs/developers/agent_assisted_development/agent-anti-patterns
6. Reason about MultistrategyVault roles
TASK: Deploy and configure a MultistrategyVault through the factory, then assign operational roles.

CONTEXT:
- Core pinned to: octant-v2-core at commit 36ed6ad6665661a18f83394d561fa75c68ccf4ac (tag 1.2.0-develop.15)
- MultistrategyVault MUST be deployed through a MultistrategyVaultFactory instance
- Do NOT deploy MultistrategyVault directly — the constructor sets asset to a sentinel value, and initialize() only succeeds when asset == address(0), so "new MultistrategyVault()" produces an unusable contract

DEPLOYMENT — deployNewVault signature:
function deployNewVault(
address asset, // ERC-20 underlying — all strategies must share this asset
string memory _name, // vault share token name
string memory symbol, // vault share token symbol
address roleManager, // initial role manager — must grant operational roles
uint256 profitMaxUnlockTime // seconds for profit unlocking (0 to disable)
) external returns (address);

The factory creates an EIP-1167 minimal proxy clone and calls initialize() on it. The returned address is your usable vault.

POST-DEPLOY ROLE ASSIGNMENT (required before any operations):
The roleManager must grant roles before you can add strategies, rebalance, or report.

Key roles to assign:
- ADD_STRATEGY_MANAGER — can call add_strategy()
- QUEUE_MANAGER — can call set_default_queue(), set_use_default_queue()
- DEBT_MANAGER — can call update_debt()
- MAX_DEBT_MANAGER — can call update_max_debt_for_strategy()
- REPORTING_MANAGER — can call process_report()
- DEPOSIT_LIMIT_MANAGER — can call set_deposit_limit()
- MINIMUM_IDLE_MANAGER — can call set_minimum_total_idle()
- EMERGENCY_MANAGER — can call shutdown_vault()

Example role grants:
vault.add_role(ops, IMultistrategyVault.Roles.ADD_STRATEGY_MANAGER);
vault.add_role(ops, IMultistrategyVault.Roles.QUEUE_MANAGER);
vault.add_role(ops, IMultistrategyVault.Roles.DEBT_MANAGER);
vault.add_role(ops, IMultistrategyVault.Roles.MAX_DEBT_MANAGER);
vault.add_role(ops, IMultistrategyVault.Roles.REPORTING_MANAGER);

OPERATING WORKFLOW:
1. Deploy vault via factory
2. Grant roles from roleManager
3. add_strategy(strategyAddress, true) — register strategy and add to queue
4. update_max_debt_for_strategy(strategyAddress, maxDebt) — set debt ceiling
5. set_default_queue([...]) — configure withdrawal order
6. update_debt(strategyAddress, targetDebt, maxLoss) — allocate capital
7. process_report(strategyAddress) — realize profit/loss periodically

EDIT BOUNDARIES:
- You may edit: deployment scripts, ops scripts, .env
- Do NOT modify: src/core/MultistrategyVault.sol, src/core/MultistrategyLockedVault.sol, src/factories/MultistrategyVaultFactory.sol, anything under dependencies/octant-v2-core/

VERIFICATION:
After each step, call the matching view function and confirm values:
- totalAssets() — total vault value
- strategies(address) — strategy debt info
- get_default_queue() — current withdrawal queue
- totalIdle() — idle assets in vault
- Check role assignments by calling the vault's role view functions

IMPORTANT:
- Do not deploy MultistrategyVault directly — always use MultistrategyVaultFactory
- Do not skip role assignment — operations will revert with NotAllowed()
- Do not treat roleManager as optional
- Report exact error messages if anything fails. Do not guess fixes.
- See anti-patterns: /docs/developers/agent_assisted_development/agent-anti-patterns
7. Build a custom TAM (Tokenized Allocation Mechanism)
TASK: Implement a custom Tokenized Allocation Mechanism (TAM) extending BaseAllocationMechanism.

CONTEXT:
- Core pinned to: octant-v2-core at commit 36ed6ad6665661a18f83394d561fa75c68ccf4ac (tag 1.2.0-develop.15)
- AllocationMechanismFactory address: 0x30B980fe1CaF8Fa275e9a364187DB953d08C3ACE
- TAM development happens inside octant-v2-core (no standalone TAM starter template)

OBTAINING THE IMPLEMENTATION ADDRESS:
Call tokenizedAllocationImplementation() on the AllocationMechanismFactory at 0x30B980fe1CaF8Fa275e9a364187DB953d08C3ACE.
That returns the shared TokenizedAllocationMechanism singleton address to pass into your constructor.

CONSTRUCTOR PATTERN:
Your mechanism inherits BaseAllocationMechanism and takes two arguments:
constructor(address implementation_, AllocationConfig memory config_)
BaseAllocationMechanism(implementation_, config_)

AllocationConfig STRUCT FIELDS:
asset (IERC20) — underlying ERC-20 for deposits and redemptions
name (string) — ERC-20 name for the mechanism's share token
symbol (string) — ERC-20 symbol for the mechanism's share token
votingDelay (uint256) — seconds between deployment and voting opens (must be >= 1)
votingPeriod (uint256) — duration of the voting window in seconds
quorumShares (uint256) — minimum For-shares for quorum (18-decimal share units)
timelockDelay (uint256) — seconds between finalization and redemption start
gracePeriod (uint256) — seconds during which redemptions are allowed
owner (address) — initial owner of the mechanism

REQUIRED HOOKS TO IMPLEMENT:
- _beforeSignupHook(address user) returns (bool)
- _getVotingPowerHook(address user, uint256 deposit) returns (uint256)
- _beforeProposeHook(address proposer) returns (bool)
- _validateProposalHook(uint256 pid) returns (bool)
- _processVoteHook(uint256 pid, address voter, VoteType choice, uint256 weight, uint256 oldPower) returns (uint256 newPower)
- _hasQuorumHook(uint256 pid) returns (bool)
- _beforeFinalizeVoteTallyHook() returns (bool)
- _convertVotesToShares(uint256 pid) returns (uint256 sharesToMint)
- _getRecipientAddressHook(uint256 pid) returns (address)
- _requestCustomDistributionHook(address, uint256) returns (bool handled, uint256 assetsTransferred)
- _calculateTotalAssetsHook() returns (uint256)

STORAGE PATTERN:
Use unstructured storage slots to avoid collisions with the core layout:
bytes32 private constant MY_SLOT = keccak256("tam.mechanism.my-mechanism.storage.v1");
struct MyStorage { ... }
function _s() private pure returns (MyStorage storage s) {
bytes32 slot = MY_SLOT;
assembly { s.slot := slot }
}

CONVENIENCE HELPERS from BaseAllocationMechanism:
- _getVotingPower(address user) — current voting power
- _proposalExists(uint256 pid) — check if proposal ID is valid
- _getProposal(uint256 pid) — full Proposal struct
- _getProposalCount() — number of proposals created
- _tokenizedAllocation() — typed reference to the shared implementation (use for owner(), management(), keeper())

EDIT BOUNDARIES:
- You may edit/create files under: src/mechanisms/, test/, script/, .env
- Do NOT modify anything under dependencies/octant-v2-core/

VERIFICATION:
1. forge build --sizes
2. forge test --match-contract YourMechanismName -vvv

IMPORTANT:
- The factory at 0x30B980fe1CaF8Fa275e9a364187DB953d08C3ACE has built-in deploy methods for Octant's packaged mechanisms. For a CUSTOM mechanism, deploy your contract directly and pass the implementation address into the constructor.
- votingDelay must be >= 1 (0 is rejected by the shared core)
- Report exact error messages if anything fails. Do not guess fixes.
- See anti-patterns: /docs/developers/agent_assisted_development/agent-anti-patterns