// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; /// @title OutreachTrialBoard /// @notice A public registry of community-outreach trials for Musechain muses. /// @dev Bounded string lengths, enumerated response statuses, full CRUD-style /// reads. No payable functions, no value flows, no personal data. contract OutreachTrialBoard { enum Status { Planned, Invited, Responded, Confirmed, Declined, Archived } struct Trial { uint256 id; string community; string route; string invitation; string date; Status status; string nextAction; uint256 createdAt; uint256 updatedAt; } uint256 public constant MAX_COMMUNITY = 80; uint256 public constant MAX_ROUTE = 160; uint256 public constant MAX_INVITATION = 500; uint256 public constant MAX_DATE = 32; uint256 public constant MAX_NEXT_ACTION = 500; Trial[] private _trials; event TrialCreated(uint256 indexed id, string community); event TrialUpdated(uint256 indexed id, Status status, string nextAction); /// @notice Register a new outreach trial. function createTrial( string calldata community, string calldata route, string calldata invitation, string calldata date ) external returns (uint256 id) { uint256 cl = bytes(community).length; require(cl > 0 && cl <= MAX_COMMUNITY, "community: 1-80 chars"); require(bytes(route).length <= MAX_ROUTE, "route: max 160 chars"); require(bytes(invitation).length <= MAX_INVITATION, "invitation: max 500 chars"); require(bytes(date).length <= MAX_DATE, "date: max 32 chars"); id = _trials.length; _trials.push( Trial({ id: id, community: community, route: route, invitation: invitation, date: date, status: Status.Planned, nextAction: "", createdAt: block.timestamp, updatedAt: block.timestamp }) ); emit TrialCreated(id, community); } /// @notice Update the response status of a trial. function updateStatus(uint256 id, Status status) external { require(id < _trials.length, "no such trial"); require(uint8(status) <= uint8(Status.Archived), "bad status"); Trial storage t = _trials[id]; t.status = status; t.updatedAt = block.timestamp; emit TrialUpdated(id, status, t.nextAction); } /// @notice Set the next action for a trial. function updateNextAction(uint256 id, string calldata nextAction) external { require(id < _trials.length, "no such trial"); require(bytes(nextAction).length <= MAX_NEXT_ACTION, "nextAction: max 500 chars"); Trial storage t = _trials[id]; t.nextAction = nextAction; t.updatedAt = block.timestamp; emit TrialUpdated(id, t.status, nextAction); } /// @notice Read one trial. function getTrial(uint256 id) external view returns (Trial memory) { require(id < _trials.length, "no such trial"); return _trials[id]; } /// @notice How many trials are registered. function trialCount() external view returns (uint256) { return _trials.length; } /// @notice Paginated list of trials, oldest first. function listTrials(uint256 offset, uint256 limit) external view returns (Trial[] memory out) { uint256 total = _trials.length; if (offset >= total || limit == 0) { return new Trial[](0); } uint256 end = offset + limit > total ? total : offset + limit; out = new Trial[](end - offset); for (uint256 i = offset; i < end; i++) { out[i - offset] = _trials[i]; } } }