8000 Mission layer M9.9: the six fixed roles, their wiring and two gates by adeelahmad · Pull Request #74 · adeelahmad/aloop · GitHub
[go: up one dir, main page]

Skip to content

Mission layer M9.9: the six fixed roles, their wiring and two gates - #74

Merged
adeelahmad merged 14 commits into
masterfrom
claude/m9-roles-9u038t
Oct 20, 2026
Merged

adeelahmad merged 14 commits into
masterfrom
claude/m9-roles-9u038t

Conversation

@adeelahmad
@adeelahmad adeelahmad commented Oct 20, 2026 •
Copy link
Copy Markdown
Owner

Requested by Adeel · project thread

Before: the mission layer (ROADMAP §4.12) had its core and areas but no roles. Nothing stopped a builder from writing an evaluation, a grader from editing source, or any agent from running the human's approval steps.

After: the six fixed roles from the spec ship as personas, each with an eval suite, a model route and hook matchers on Claude Code, Codex and OpenCode. The roles are the Market Researcher, Product Tester, State Assessor, Evaluator, Mission Planner and Ethics Reviewer. Two new gates enforce role separation outside the model:

  • gate-mission-scope (PreToolUse) is the role path guard.

    • A mission role writes only its own dirs (plugin/mission/roles.json). It never writes source, mission.json, another role's dir, a task worktree, the event log or the gates' state. It never commits and never runs a human's step.
    • The human's steps are mission approve and an area's approve, confirm, consent, veto, restore or revoke. They also include tick --force, events authority, and an events emit or decide with role human.
    • A builder never writes docs/agents/mission/ and runs only read-only mission commands.
    • The supervisor never hand-writes a role's artifact.
    • No one hand-writes the ledger or the captured prompts a grant reads (M9.17), runs a hook's transcripts command, or hand-emits a mission-layer event. Where a mission is set, no one starts a host session from a tool call.
    • Bash is read the way a shell reads it, by a new shell reader (_shellread.py). It handles quotes, comments, line continuations, heredocs, wrappers, bash -c, eval, interpreters, variables and $(mktemp).
  • gate-mission-artifact records what exists when a role starts. At the role's stop it checks three things:

    • The role wrote an artifact in its own paths.
    • Each artifact passes the core contract and its area's own check. The check runs once per artifact, with its id or subject (area_checks in roles.json).
    • Nothing was deleted, no other role's artifact changed, and the ledger still verifies.

    A status change is not counted against the role when an event of the run explains it: a human's step, or the role's own command (baseline apply freezing journeys, an ethics hard line blocking its goal). An ethics hard line is the reviewer's finding, not a defect, and a derived goal whose only blocks are its pending ethics review waits on the Ethics Reviewer, not the planner. A pass records artifact. 8000 created|updated (role, agent id, sha256) as provenance. A run with no start record is blocked.

How: plugin/bin/_mission_roles.py classifies paths against the repo's worktrees and diffs the start record. Role lists in _routes, install, gate-dispatch, selfcheck, budget and the OpenCode plugin name the six roles. OpenCode records a role's start on its first chat.message, once the agent is known. A mission write never arms the sprint lock.

Calls I made (recorded in §4.12):

  • The four judging roles take the planning route and the two measuring roles the worker route.
  • The Mission Planner also writes docs/agents/intents/, but only its source: mission intents count as its work.
  • A human event during a run is named in a note and in gate.passed, not blocked: the gate cannot tell who ran it (M9.17).
  • New gap items: M9.40 (tick accepts a role's artifact only with its provenance), M9.41 (serialize overlapping role runs; a write made inside a program is invisible to the path guard) and M9.42 (the Product Tester's research new --role product-tester and a judge run record need per-artifact ownership; no role sets ALOOP_JUDGE_CMD). M9.42 and the second review's Bash-reader findings come next in a follow-up PR.

Evidence:

🤖 Generated with Claude Code

https://claude.ai/code/session_0197nSutKR1QAAqgJhFZ8fPj

The six mission roles (ROADMAP §4.12) are personas with eval suites,
model routes, hook matchers on Claude Code, Codex and OpenCode, and
Codex/OpenCode agents from `aloop install`. plugin/mission/roles.json
names the paths each role may write.

gate-mission-scope (PreToolUse) is the role path guard: a mission role
writes only its own dirs, never source, mission.json or another role's
dir, never commits or runs a human's approval; a builder never writes
docs/agents/mission/ or runs a writing mission command; the supervisor
never hand-writes a role's artifact.

gate-mission-artifact records what exists at a role's start and, at
its stop, requires an artifact in its own paths that passes the core
contract and its area's check, refuses a deleted artifact or a change
to another role's, and records artifact.created|updated as provenance.

Test: scripts/test/mission-roles.test.py (26 checks), 5 new OpenCode
end-to-end checks; make test-hosts, conformance-free lints and
claude plugin validate --strict pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197nSutKR1QAAqgJhFZ8fPj
@adeelahmad adeelahmad self-assigned this Oct 20, 2026
…on skill

The roles test compared a resolved plugin path with an unresolved temp dir, so it failed on macOS
(/var is a symlink to /private/var). The OpenCode skill list moves to one table that also names
the mission skill (aloop-mission, from M9.12) and skips a skill the plugin does not ship yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197nSutKR1QAAqgJhFZ8fPj
adeelahmad pushed a commit that referenced this pull request Oct 20, 2026
…ount; due never runs past a cap

- due exits 2 with BLOCK[mission-due] when the facts cannot be read, instead
  of 1 ("not due"), and the cron line logs due as well as the host session.
- At STOP_BUDGET due starts no session: it records the stop once itself.
- The token cap counts cache writes (on Claude Code nearly all new input).
- Stray event lines, a null sprints list, string counts in the ledger, a
  mistyped cap or cadence, an unreadable mission.json: SET_MISSION or the
  next step, never a traceback (_events.read skips non-object lines).
- Dotted, routed and Bedrock (global., us-gov.) model ids are priced.
- Second-resolution stamps tie toward "after".
- Gap items renumbered M9.19, M9.20 (#74 takes M9.18); new M9.21 (fast mode
  and web search are not in the ledger).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Aw6CiQcdpzdzZZXKf8WbUM
…ixes)

Two adversarial reviews of the role path guards found commands the old splitter let through
(timeout/env/nice wrappers, if/for bodies, comments with an apostrophe, line continuations,
`python3 …/bin/mission approve`, `bash -c`, `$VAR` targets, `sed -Ei`, `>|`) and legitimate ones it
refused (a rival run in a mktemp scratch dir, `sed -i -e`, `git branch --show-current`, `truncate -s 0`).
gate-mission-scope now reads a command with _shellread.py (quotes, escapes, comments, heredocs,
reserved words, subshells, $(…) read as commands) and resolves variables, $(pwd) and $(mktemp).
A mission role no longer writes the event log or the gates' state.

gate-mission-artifact: an OpenCode child's start record waits until its role is known (a role-less
record excused every change); intents other than `source: mission` are no role's; a run of the
owner that overlapped (open, or stopped since) excuses a change; a status-only change to another
role's artifact counts unless the human approved it; a human's approval of a role's own file is not
its write; a human event during a run is named in a note and gate.passed (M9.17 attributes it); no
start record blocks; a stuck release closes the run; a pass re-baselines it; checks share one 540s
deadline; intents are checked in process (no intent.normalized); bad gates.jsonl lines are skipped.
M9.19 records what is left: overlapping runs that share a path, and writes made inside a program.

Tests: mission-scope-bash.test.py (175 commands), mission-roles.test.py (29 checks),
opencode-plugin.test.mjs (25).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197nSutKR1QAAqgJhFZ8fPj
# Conflicts:
#	CHANGELOG.md
#	docs/ROADMAP.md
#	docs/TRACKING.md
Merged master (M9.2, M9.3, M9.6, M9.10) and took up the asks from its review:
- no one hand-writes docs/agents/mission/ledger.jsonl (its own message), and gate-mission-artifact
  blocks a stop when the ledger changed during the run and no longer verifies (new problems only);
- `mission baseline restore` joins the human verbs; `events authority` and an `events emit` with
  role human (or unknown) or a human decision's type are blocked for every dispatched agent;
- a supervisor write under docs/agents/mission/ never arms the sprint lock.

Test: mission-roles.test.py (31 checks; one runs each role through the real journeys, baseline,
assess and evaluate areas and checks its stop passes), mission-scope-bash.test.py (196 rows, 21
new, 11 failing on the previous gate); make test-hosts, lints, conformance 78/78 and
claude plugin validate --strict pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197nSutKR1QAAqgJhFZ8fPj
A dispatched agent's `events decide … --role human` (or an unknown role) is blocked like a human
`events emit`. Test: mission-scope-bash.test.py (198 rows, 2 new).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197nSutKR1QAAqgJhFZ8fPj
Already blocked for every dispatched agent (tick is the loop's); the row keeps it so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197nSutKR1QAAqgJhFZ8fPj
…ates to its areas

- An area's check runs once per artifact with its id or subject (roles.json `area_checks`):
  `mission ethics check` needs `--subject`, so every Ethics Reviewer stop failed with exit 64.
  An ethics hard line (exit 2, state hard) is the reviewer's finding, a note, not a defect.
- A status change an event of the run explains is not another role's write: a human's step, or
  the role's own command (M9.8's tick hands `baseline apply` to the State Assessor, which freezes
  the researcher's journeys; a hard line blocks the planner's goal). Anything else still blocks.
- No dispatched agent hand-emits a mission-layer event (mission.*, artifact.*, the areas').
- ROADMAP: my gap rows move to M9.26 and M9.27 (master took M9.18/M9.19, #75 takes M9.20–25).

Test: mission-roles.test.py (32 checks; the new one runs the real ethics, baseline apply and
journeys retire), mission-scope-bash.test.py (202 rows); make ci and conformance 78/78 pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197nSutKR1QAAqgJhFZ8fPj
…rgery paths

PR #82 (M9.17) makes a mission grant need the human's own words: a prompt line the
UserPromptSubmit hook captured. gate-mission-scope now denies the ways around that, for every
agent and the supervisor:
- a write (Write/Edit/patch or a Bash write) under <run data>/transcripts/, the captured prompts;
- a Bash `transcripts prompt|record|snapshot|stop|stage-in`, the hooks' own commands;
- where a mission is set, a host session started from a tool call (`claude -p`, `codex exec`,
  `opencode run`, or any prompt), since its prompt fires UserPromptSubmit too. A host's tools
  (`claude plugin validate`, `--version`, `opencode models`) stay allowed; no mission, no rule.

Test: mission-scope-bash.test.py (220 rows; 13 of the 18 new ones fail on the previous gate),
mission-roles.test.py (32 checks); make ci, conformance 78/78 and plugin validate --strict pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197nSutKR1QAAqgJhFZ8fPj
Docs only: ROADMAP takes master's M9.8 and M9.20–M9.25 rows beside this track's M9.9, M9.26 and
M9.27; TRACKING marks both M9.8 and M9.9 implemented. make ci (conformance 78/78) passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197nSutKR1QAAqgJhFZ8fPj
@adeelahmad
adeelahmad marked this pull request as ready for review October 20, 2026 04:12
…eck gets its id

A derived goal's check waits on its ethics review, which is the Ethics
Reviewer's step: area_checks marks those blocks foreign, so the planner's
stop notes them instead of blocking. Gap rows renumbered to M9.30/M9.31.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197nSutKR1QAAqgJhFZ8fPj
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197nSutKR1QAAqgJhFZ8fPj
The role runs use the real research and judge commands: the Scout's
report is written through the CLI, and research and judge checks get
the artifact's id. Gap rows move to M9.40/M9.41 (M9.28-M9.35 are taken
by open PRs); M9.42 records the shared research dir and judge runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197nSutKR1QAAqgJhFZ8fPj
adeelahmad pushed a commit that referenced this pull request Oct 20, 2026
…87 M9.28-30, #74 M9.40-42, #86 M9.50+)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Aw6CiQcdpzdzZZXKf8WbUM
adeelahmad pushed a commit that referenced this pull request Oct 20, 2026
The section B approvals now go through the human's captured words,
as M9.17 requires.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197nSutKR1QAAqgJhFZ8fPj
@adeelahmad
adeelahmad merged commit af7cc2b into master Oct 20, 2026
5 checks passed
adeelahmad pushed a commit that referenced this pull request Oct 20, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Aw6CiQcdpzdzZZXKf8WbUM
adeelahmad pushed a commit that referenced this pull request Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0