1---2name: onsite-propose3description: Use to turn audit findings or signals into concrete gated change proposals - "propose fixes for /pricing", /organic-os:propose.4---56# Propose on-page changes (creates gated items; applies nothing)781. Input: audit findings, a signal reference, or a user request naming URLs.92. For each change, draft the exact after-state: new title (<= 60 chars),10 new meta description (<= 155 chars), canonical, focus keyword, schema11 JSON-LD payload, or a content edit (quote the exact before/after text).122b. Decision check, BEFORE any `create_item` call. For every target, search13 the brain's decision memory:14 `PYTHONPATH="$CLAUDE_PLUGIN_ROOT/lib" python3 -c "..."` snippet importing15 `core.decisions` - `search(<brain>, [<page path>, <focus keyword>,16 <what the fix does>])`. It returns prior decisions newest first with17 `date`, `choice`, `scope`, `item`, and the `rationale`.18 A hit whose `choice` is `rejected` means a human already refused this19 work. Exactly two paths are allowed, never a third:20 - SKIP it, and say so in the run report: name the page, the decision21 date, and the recorded reason.22 - Or CREATE it with a line in the proposal body reading "previously23 rejected on <date> because <reason>; proposing again because <what24 changed>". What changed must be a concrete new fact - a new signal, a25 ranking move, a competitor or site change - never "worth another26 look".27 Silently re-creating a rejected proposal is forbidden. A hit with any28 other `choice` is context to quote in the body, not a blocker.293. One proposal item per page: `create_item(kind="onpage-fix", target=<url>,30 body=<before/after table + rationale + expected effect + falsifiability>)`.31 `create_item` is the ONLY birth path - items are born `proposed` with an32 empty approvals list; never write a `proposals/` file directly or set33 any other status at creation.344. Rebuild queue (`create_item` births items; the rebuild is what puts them in35 `approvals/queue.md`). Notify per the profile approval channel with ONE call:36 `PYTHONPATH="$CLAUDE_PLUGIN_ROOT/lib" python3 -c "..."` importing37 `core.approval` and calling `notify_pending(<brain>, send)`, where `send`38 is a callable taking `(path, item)` and delivering over the channel chosen39 below. It sends every un-notified proposed item and marks each as it goes.40 An item is marked notified ONLY after its send returns without raising, so41 a failed send is retried on the next run rather than lost, and re-runs42 never re-notify. Do not call `is_notified` or `mark_notified` by hand.43 Channel selection:44 - in-session: present now with AskUserQuestion (approve/reject each)45 - telegram: send via core.telegram `send_item` (token from env file), then46 poll with `core.approval.process_telegram_decisions` on the next run to47 collect replies (offset persisted automatically)48 - pr-merge: commit the proposal file on a branch, open a PR (gh pr create)49 - slack/email: post/send a summary via the available connector; approval50 happens in-session or by channel reply read at the next run514b. Image and alt-text fixes (action type: image-fix). Audit findings52 from the images checks - a missing or empty alt (skills/onsite-audit53 step 2), an imageless 500+ word explainer (step 3.3) - become one54 `onpage-fix` proposal per page whose body declares `action:55 image-fix` and carries one line per image:56 - Missing alt text: the image's src and media id (from the CMS57 adapter's `get_media`), the current alt (empty), and the proposed58 alt text. Ground the proposed alt in the surrounding content:59 describe what the image shows for someone who cannot see it, in60 one sentence. Never keyword-stuffed - alt text is accessibility61 text first, and a keyword appears only when the image is genuinely62 about it.63 - Missing in-content image: reference the ce-image brief path64 (skills/ce-image writes `<slug>-image-brief.md`) as the creation65 route, and name where in the post the image belongs. Creation66 happens through that brief, outside the apply path: apply only67 places a file that already exists (see the image-fix section in68 skills/onsite-apply), so the proposal must be executable once the69 asset exists, and honest about waiting until it does.70 Same gate as every proposal: born proposed via `create_item`,71 approved by a human, executed by onsite-apply.725. Record any in-session decisions immediately via the contract CLI:73 `PYTHONPATH="$CLAUDE_PLUGIN_ROOT/lib" python3 -m core approve <item-path>74 --actor <user> --channel in-session` (or `reject`, with `--note` for any75 reason given). Never edit brain frontmatter directly. The contract CLI is76 the only write path for status and approvals.