create-open-autonomy 2.3.11 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -29,8 +29,8 @@ CHANGELOG.md what shipped
29
29
  AGENTS.md the agent's rules for this repository
30
30
  LICENSE Apache-2.0, seeded; the project's own
31
31
  package.json, test/ the project's own check (`bun run check`), starting with one test
32
- hermes/ the agent: SOUL.md, its two skills (develop, pm; a project's own skills live beside them, in hermes/skills/<project>/, and are the project's), profiles/treasurer (the second profile: the one that pays), kanban.seed.json (the board's first tasks, in order),
33
- cron/jobs.seed.json (the PM, hourly), config.yaml (the model: the project's own choice), the seed hook
32
+ hermes/ the agent: SOUL.md, its three skills (develop, pm, community; a project's own skills live beside them, in hermes/skills/<project>/, and are the project's), profiles/treasurer (the second profile: the one that pays), kanban.seed.json (the board's first tasks, in order),
33
+ cron/jobs.seed.json (the PM, hourly; the community desk, every quarter hour), config.yaml (the model: the project's own choice), the seed hook
34
34
  .open-autonomy/ the platform connection: config.yaml (account, publish policy, the model and rail bounds the platform holds the project's funds to), reporter.ts (the publisher:
35
35
  sessions, the board, the setup), mint-key.ts (the key, the adopter way), start.ts (the agent's four
36
36
  processes, the one way it starts), the vendored SDK, kit.json (which kit, version and parameters made this repository)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-open-autonomy",
3
- "version": "2.3.11",
3
+ "version": "2.4.0",
4
4
  "description": "The default Open Autonomy starter kit: a complete repository that runs its own Hermes agent against the platform, with the SDK wired in. `bun create open-autonomy <dir>` scaffolds one.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
package/src/kit.ts CHANGED
@@ -9,7 +9,7 @@
9
9
  import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
10
10
  import { dirname, join, relative, resolve } from 'node:path';
11
11
 
12
- export const KIT = { name: 'hermes', version: '2.3.11' } as const;
12
+ export const KIT = { name: 'hermes', version: '2.4.0' } as const;
13
13
  export const KIT_FILE = '.open-autonomy/kit.json';
14
14
  const TEMPLATE = resolve(import.meta.dir, '..', 'template');
15
15
 
@@ -18,8 +18,8 @@ export interface KitRecord { kit: string; version: string; params: KitParams; di
18
18
 
19
19
  // What the kit keeps current. Everything else in the template is seeded once.
20
20
  // A project's own, seeded once: its config (the treasurer's too: the model is the project's choice for both profiles),
21
- // its board seed, its schedule, and any skill of its own outside hermes/skills/open-autonomy/ (the kit's two).
22
- const OWNED = [/^hermes\/(?!config\.yaml$|kanban\.seed\.json$|cron\/jobs\.seed\.json$|profiles\/treasurer\/config\.yaml$|skills\/(?!open-autonomy\/))/, /^\.open-autonomy\/(reporter\.ts|mint-key\.ts|start\.ts|package\.json|sdk\/)/, /^container\//, /^\.github\/workflows\/(ci|land)\.yml$/];
21
+ // its board seed, its schedule, and any skill of its own outside hermes/skills/open-autonomy/ (the kit's three).
22
+ const OWNED = [/^hermes\/(?!config\.yaml$|kanban\.seed\.json$|cron\/jobs\.seed\.json$|profiles\/treasurer\/config\.yaml$|skills\/(?!open-autonomy\/))/, /^\.open-autonomy\/(reporter\.ts|mint-key\.ts|start\.ts|community\.ts|package\.json|sdk\/)/, /^container\//, /^\.github\/workflows\/(ci|land)\.yml$/];
23
23
  export const isOwned = (rel: string): boolean => OWNED.some((re) => re.test(rel));
24
24
 
25
25
  export function validateParams(p: Partial<KitParams>): KitParams {
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env bun
2
+ // The community desk's doors, from the shell: this repository's issues and discussions on GitHub, through the API
3
+ // the environment names — GITHUB_API_URL and GITHUB_TOKEN. In the running stack that is the valve's GitHub port and
4
+ // the word `valve`: the agent's own GitHub App answers, its key never here. Issues over REST; discussions over
5
+ // GraphQL, the only API GitHub serves them on.
6
+ //
7
+ // bun .open-autonomy/community.ts poll # every issue, comment and discussion since the
8
+ // # last look: NEW lines, then COMMUNITY_POLL_DONE
9
+ // bun .open-autonomy/community.ts comment <issue> <text…> # a comment on an issue
10
+ // bun .open-autonomy/community.ts discuss <discussion> <text…> # a comment on a discussion
11
+ // bun .open-autonomy/community.ts mark # the last look is now
12
+ //
13
+ // The cursor lives in the agent's home ($HERMES_HOME/community-cursor.json), else beside the project.
14
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
15
+ import { resolve } from 'node:path';
16
+
17
+ const api = (process.env.GITHUB_API_URL ?? 'https://api.github.com').replace(/\/$/, '');
18
+ const token = process.env.GITHUB_TOKEN ?? '';
19
+ if (!token) { console.error('community: no GITHUB_TOKEN — the desk has no GitHub door (a github-app.json beside the keys gives it one through the valve)'); process.exit(3); }
20
+ const project = resolve(import.meta.dir, '..');
21
+ const account = /^account:\s*(\S+)/m.exec(readFileSync(resolve(project, '.open-autonomy', 'config.yaml'), 'utf8'))?.[1] ?? '';
22
+ if (!account) throw new Error('community: .open-autonomy/config.yaml names no account');
23
+ const [owner, name] = account.split('/');
24
+ const cursorFile = resolve(process.env.HERMES_HOME ?? project, process.env.HERMES_HOME ? 'community-cursor.json' : '.community-cursor.json');
25
+ const cursor = (): string => (existsSync(cursorFile) ? (JSON.parse(readFileSync(cursorFile, 'utf8')) as { since: string }).since : '1970-01-01T00:00:00Z');
26
+ const headers = { authorization: `Bearer ${token}`, accept: 'application/vnd.github+json', 'content-type': 'application/json' };
27
+ async function github<T>(method: string, path: string, body?: unknown): Promise<T> {
28
+ const res = await fetch(`${api}${path}`, { method, headers, body: body === undefined ? undefined : JSON.stringify(body) });
29
+ if (!res.ok) throw new Error(`${method} ${path} → ${res.status} ${(await res.text()).slice(0, 200)}`);
30
+ return (await res.json()) as T;
31
+ }
32
+ async function graphql<T>(query: string, variables: Record<string, unknown>): Promise<T> {
33
+ const res = await fetch(`${api}/graphql`, { method: 'POST', headers, body: JSON.stringify({ query, variables }) });
34
+ const out = (await res.json()) as { data?: T; errors?: Array<{ message: string }> };
35
+ if (!res.ok || out.errors?.length) throw new Error(`graphql → ${res.status} ${out.errors?.map((e) => e.message).join('; ') ?? ''}`);
36
+ return out.data as T;
37
+ }
38
+ interface Discussion { id: string; number: number; title: string; body: string; createdAt: string | null; category: { name: string } | null; comments: { nodes: Array<{ id: string; body: string; createdAt: string | null }> } }
39
+ const discussions = () => graphql<{ repository: { discussions: { nodes: Discussion[] } } }>(
40
+ `query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { discussions(first: 50) { nodes { id number title body createdAt category { name } comments(first: 50) { nodes { id body createdAt } } } } } }`,
41
+ { owner, name },
42
+ ).then((d) => d.repository.discussions.nodes);
43
+ const after = (at: string | null | undefined, since: string): boolean => !at || at > since;
44
+ const firstLine = (s: string): string => (s ?? '').split('\n')[0]!.slice(0, 120);
45
+
46
+ const [command, ...rest] = process.argv.slice(2);
47
+ if (command === 'poll') {
48
+ const since = cursor();
49
+ const issues = await github<Array<{ number: number; title: string; body: string | null; created_at: string; pull_request?: unknown; user?: { login?: string } }>>('GET', `/repos/${account}/issues?state=open&per_page=50&since=${encodeURIComponent(since)}`);
50
+ for (const i of issues) if (!i.pull_request && after(i.created_at, since)) console.log(`NEW issue #${i.number} ${JSON.stringify(i.title)} by ${i.user?.login ?? 'someone'}: ${firstLine(i.body ?? '')}`);
51
+ for (const i of issues) {
52
+ if (i.pull_request) continue;
53
+ const comments = await github<Array<{ body: string; created_at: string; user?: { login?: string } }>>('GET', `/repos/${account}/issues/${i.number}/comments?per_page=50&since=${encodeURIComponent(since)}`);
54
+ for (const c of comments) if (after(c.created_at, since)) console.log(`NEW comment on #${i.number} by ${c.user?.login ?? 'someone'}: ${firstLine(c.body)}`);
55
+ }
56
+ for (const d of await discussions()) {
57
+ if (after(d.createdAt, since)) console.log(`NEW discussion #${d.number} ${JSON.stringify(d.title)} (${d.category?.name ?? 'general'}): ${firstLine(d.body)}`);
58
+ for (const c of d.comments.nodes) if (after(c.createdAt, since)) console.log(`NEW reply on discussion #${d.number}: ${firstLine(c.body)}`);
59
+ }
60
+ console.log(`COMMUNITY_POLL_DONE since ${since}`);
61
+ } else if (command === 'comment' && rest.length >= 2) {
62
+ const n = Number(rest[0]);
63
+ const c = await github<{ id: number }>('POST', `/repos/${account}/issues/${n}/comments`, { body: rest.slice(1).join(' ') });
64
+ console.log(`commented on #${n} (${c.id})`);
65
+ } else if (command === 'discuss' && rest.length >= 2) {
66
+ const n = Number(rest[0]);
67
+ const d = (await discussions()).find((x) => x.number === n);
68
+ if (!d) throw new Error(`no discussion #${n}`);
69
+ const out = await graphql<{ addDiscussionComment: { comment: { id: string } } }>(`mutation($discussionId: ID!, $body: String!) { addDiscussionComment(input: { discussionId: $discussionId, body: $body }) { comment { id } } }`, { discussionId: d.id, body: rest.slice(1).join(' ') });
70
+ console.log(`replied on discussion #${n} (${out.addDiscussionComment.comment.id})`);
71
+ } else if (command === 'mark') {
72
+ writeFileSync(cursorFile, `${JSON.stringify({ since: new Date().toISOString() })}\n`);
73
+ console.log(`marked: the last look is now (${cursorFile})`);
74
+ } else {
75
+ console.error('usage: community poll | comment <issue> <text…> | discuss <discussion> <text…> | mark');
76
+ process.exit(2);
77
+ }
@@ -172,7 +172,9 @@ class Followed {
172
172
  const ready = msgs.slice(0, n);
173
173
  const turns = ready.flatMap(turnsOf);
174
174
  if (turns.length) {
175
- this.item ??= itemIn(turns);
175
+ // Only a board run serves an item; a scheduled session (the PM over the whole board) mentions branches and
176
+ // tasks without being about one.
177
+ if (sourceOf(this.d) === 'board') this.item ??= itemIn(turns);
176
178
  this.sha ??= shaIn(turns);
177
179
  await this.session!.turns(turns, this.item);
178
180
  this.seq = this.session!.seq;
@@ -6,6 +6,11 @@
6
6
  //
7
7
  // bun .open-autonomy/start.ts [--home <dir>] [--secrets <dir>] [--project <dir>] [--origin <url>] [--as <user>] [--valve <port>]
8
8
  //
9
+ // <secrets>/github-app.json, when present, is the agent's own GitHub identity for its community desk (a GitHub App
10
+ // installed on the repository: app_id, installation_id, repository, private_key): the valve serves it on the fourth
11
+ // port as api.github.com, and the home's .env points GITHUB_API_URL there with GITHUB_TOKEN=valve — every comment the
12
+ // desk posts is the app's, and the key never enters the agent.
13
+ //
9
14
  // <secrets>/codex.json, when present, is the owner's ChatGPT/Codex subscription login (the Codex CLI's auth.json
10
15
  // tokens): the valve serves it on the third port, and the home's .env names it (HERMES_CODEX_BASE_URL) for a custom
11
16
  // provider in the project's config that speaks the Codex protocol — the model runs on the subscription, the login
@@ -94,11 +99,15 @@ if (existsSync(committed)) {
94
99
  }
95
100
  // The home's .env is the home's own, except the valve's three lines, which are this start's truth on every start.
96
101
  const envFile = resolve(home, '.env');
97
- const kept = existsSync(envFile) ? readFileSync(envFile, 'utf8').split('\n').filter((l) => l.trim() && !/^(OPEN_AUTONOMY_(BASE_URL|PAY_URL|KEY)|HERMES_CODEX_BASE_URL)=/.test(l)) : [];
102
+ const githubApp = existsSync(resolve(secrets, 'github-app.json'));
103
+ const managed = githubApp ? /^(OPEN_AUTONOMY_(BASE_URL|PAY_URL|KEY)|HERMES_CODEX_BASE_URL|GITHUB_API_URL|GITHUB_TOKEN)=/ : /^(OPEN_AUTONOMY_(BASE_URL|PAY_URL|KEY)|HERMES_CODEX_BASE_URL)=/;
104
+ const kept = existsSync(envFile) ? readFileSync(envFile, 'utf8').split('\n').filter((l) => l.trim() && !managed.test(l)) : [];
98
105
  // The Codex subscription's address goes in the .env too: Hermes loads the home's .env into every process it starts,
99
106
  // including the scheduler's job runners, which do not inherit the gateway's environment.
100
107
  const codexBase = existsSync(resolve(secrets, 'codex.json')) ? [`HERMES_CODEX_BASE_URL=http://127.0.0.1:${valvePort + 2}/backend-api/codex`] : [];
101
- const lines = [`OPEN_AUTONOMY_BASE_URL=${baseUrl}`, `OPEN_AUTONOMY_PAY_URL=${payUrl}`, 'OPEN_AUTONOMY_KEY=valve', ...codexBase, ...kept];
108
+ // The desk's GitHub door likewise: the valve's fourth port, as api.github.com.
109
+ const githubDoor = githubApp ? [`GITHUB_API_URL=http://127.0.0.1:${valvePort + 3}`, 'GITHUB_TOKEN=valve'] : [];
110
+ const lines = [`OPEN_AUTONOMY_BASE_URL=${baseUrl}`, `OPEN_AUTONOMY_PAY_URL=${payUrl}`, 'OPEN_AUTONOMY_KEY=valve', ...codexBase, ...githubDoor, ...kept];
102
111
  // On the first start, what the environment says about the agent's channels comes along: Discord's, and GitHub's for a community desk.
103
112
  if (!existsSync(envFile)) for (const k of Object.keys(process.env).sort()) if (/^(DISCORD_|GITHUB_TOKEN$|GITHUB_API_URL$)/.test(k) && process.env[k]) lines.push(`${k}=${process.env[k]}`);
104
113
  writeFileSync(envFile, `${lines.join('\n')}\n`);
@@ -115,12 +124,15 @@ if (!keys.length) { console.error(`start: no ${resolve(secrets, 'agent.env')}
115
124
  const codexFile = resolve(secrets, 'codex.json');
116
125
  const codexPort = valvePort + 2;
117
126
  if (existsSync(codexFile)) keys.push('--codex', `${codexFile}:${codexPort}`);
127
+ // The agent's GitHub identity: the valve mints the app's installation tokens and serves the desk's routes on the fourth port.
128
+ const githubFile = resolve(secrets, 'github-app.json');
129
+ if (githubApp) keys.push('--github-app', `${githubFile}:${valvePort + 3}`);
118
130
  spawn('valve', ['bun', resolve(import.meta.dir, 'sdk', 'valve.ts'), ...keys], {});
119
131
 
120
132
  // 5. The reporter and the gateway, as the agent.
121
133
  const env = agentEnv();
122
134
  spawn('reporter', ['bun', resolve(import.meta.dir, 'reporter.ts'), '--config', resolve(project, '.open-autonomy', 'config.yaml')], { asAgent: true, env: { ...env, OPEN_AUTONOMY_BASE_URL: baseUrl } });
123
135
  spawn('gateway', ['hermes', 'gateway', 'run'], { asAgent: true, env });
124
- say(`gateway up in ${project} as ${user?.name ?? userInfo().username}, home ${home}; the valve on :${valvePort}${existsSync(resolve(secrets, 'treasurer.env')) ? ` and :${valvePort + 1}` : ''}${existsSync(codexFile) ? `; the Codex subscription on :${codexPort}` : ''}`);
136
+ say(`gateway up in ${project} as ${user?.name ?? userInfo().username}, home ${home}; the valve on :${valvePort}${existsSync(resolve(secrets, 'treasurer.env')) ? ` and :${valvePort + 1}` : ''}${existsSync(codexFile) ? `; the Codex subscription on :${codexPort}` : ''}${githubApp ? `; the GitHub App on :${valvePort + 3}` : ''}`);
125
137
  if (!readFileSync(resolve(project, '.open-autonomy', 'config.yaml'), 'utf8').includes('account:')) say('warning: .open-autonomy/config.yaml names no account');
126
138
  await new Promise(() => {});
@@ -4,6 +4,7 @@
4
4
  - **Checks.** `bun run check` from the repository root is the project's definition of green, in under thirty seconds. It must pass before every push. Behavior is verified by running the system, not by tests written for the occasion.
5
5
  - **Verify.** State here where the project is verified: its check, and any local or twinned surface. A project that talks to a vendor keeps a world in `world/` — the twins are npm packages (`@volter/twin-world` and one `@volter/twin-<vendor>` per vendor, dev dependencies), a `world/world.json` names them, `bunx volter-world up world/world.json` brings them up — and you drive the running system in it, one action at a time; never a real API. You cannot reach production and must not try. Where an acceptance line names a surface, exercise the surface.
6
6
  - **Git.** You cannot push to `main` and must not try. Work on `agent/<task id>` off a fresh `origin/main`, commit small with the task id first in the subject, and push the branch; the landing workflow opens the pull request and it merges itself when the checks pass. Never rewrite history, never force-push.
7
+ - **The community.** `.open-autonomy/community.ts` is your door to the repository's issues and discussions (`GITHUB_API_URL` and `GITHUB_TOKEN`: in the stack, the valve's GitHub port and the word `valve` — the project's own GitHub App answers); Discord is Hermes's own. The `community` skill says what to answer, what to file and what to decline; its job runs every quarter hour.
7
8
  - **Secrets.** There are none for you to use: your model calls and your pushes are authorized outside your reach. Never read or print `.env` files or key material; your sessions are published live.
8
9
  - **Do not edit** `LICENSE`, `.github/workflows/`, `container/`, `.open-autonomy/reporter.ts`, or anything under `hermes/` except a skill a task asks you to improve.
9
10
  - **Cost.** Your calls are metered and public. Read before writing; run the check once; stop when verified.
@@ -1,9 +1,9 @@
1
1
  # This project's agent
2
2
 
3
3
  This directory is a complete Hermes home (`HERMES_HOME`), from the Open Autonomy Hermes kit. Everything the
4
- agent is lives here and is committed: `SOUL.md` (identity), `skills/` (the two things it does beyond what Hermes
5
- brings: `develop`, `pm`), `kanban.seed.json` (the board's first tasks, in order; one with a `held` reason is filed parked, the owner's to release with `hermes kanban unblock`), `cron/jobs.seed.json` (its one job: the
6
- PM, hourly), `config.yaml` (which model, through the platform; a worker takes it at dispatch, so a model change
4
+ agent is lives here and is committed: `SOUL.md` (identity), `skills/` (the three things it does beyond what Hermes
5
+ brings: `develop`, `pm`, `community`), `kanban.seed.json` (the board's first tasks, in order; one with a `held` reason is filed parked, the owner's to release with `hermes kanban unblock`), `cron/jobs.seed.json` (its jobs: the
6
+ PM, hourly; the community desk, every quarter hour), `config.yaml` (which model, through the platform; a worker takes it at dispatch, so a model change
7
7
  never strands anything), `hooks/` (the seed: the schedule and the board, on every boot, idempotent). Its runtime
8
8
  state (sessions, logs, caches, `.env`, the board's database) is git-ignored.
9
9
 
@@ -1,6 +1,6 @@
1
- You are this project's agent: the checked-in Hermes agent that builds the project you run in, month after month, on a token budget its sponsors fund through Open Autonomy. Your home is the repository you run in. Everything you are is readable there: this file, your two skills, your one scheduled job.
1
+ You are this project's agent: the checked-in Hermes agent that builds the project you run in, month after month, on a token budget its sponsors fund through Open Autonomy. Your home is the repository you run in. Everything you are is readable there: this file, your three skills, your two scheduled jobs.
2
2
 
3
- The board is the roadmap. The owner files tasks on it; your dispatcher pulls them down in order and runs each as a worker session (the develop skill), the review lane verifies every handoff, and once an hour you look at the whole board and unstick what is stuck (the pm skill). When you review, the bar is two documents: `CONSTITUTION.md`, whose invariants no change may violate and whose out-of-scope no change may enter, and `CONTRIBUTING.md`, which the diff is held to. Every acceptance line made true and verified by running the system, nothing in the diff that no line asked for, and no test that guards nothing in the constitution: test cruft is the one debt that compounds. Approve in one paragraph naming what you checked; otherwise send it back naming each failing line. You finish things: a task is done when its acceptance lines are true in the running system, not when code exists. You never invent tasks; filing is the owner's job.
3
+ The board is the roadmap. The owner files tasks on it; your dispatcher pulls them down in order and runs each as a worker session (the develop skill), the review lane verifies every handoff, and once an hour you look at the whole board and unstick what is stuck (the pm skill). Every quarter hour you are the project's face to its community — its issues, its discussions, its channel — answering where you were asked and filing what fits the constitution (the community skill). When you review, the bar is two documents: `CONSTITUTION.md`, whose invariants no change may violate and whose out-of-scope no change may enter, and `CONTRIBUTING.md`, which the diff is held to. Every acceptance line made true and verified by running the system, nothing in the diff that no line asked for, and no test that guards nothing in the constitution: test cruft is the one debt that compounds. Approve in one paragraph naming what you checked; otherwise send it back naming each failing line. You finish things: a task is done when its acceptance lines are true in the running system, not when code exists. You never invent tasks; filing is the owner's job.
4
4
 
5
5
  You spend sponsors' money. Every model call you make is metered to this project's account and shown in public. Be economical: read before you write, run the check once, and stop when the work is verified. Do not loop on a failure you cannot explain; say what you found and block the task with what is missing.
6
6
 
@@ -4,7 +4,18 @@
4
4
  "name": "pm",
5
5
  "prompt": "You are this project's PM for the hour. Run the pm skill: read the board, unstick what is stuck, report.",
6
6
  "schedule": "every 60m",
7
- "skills": ["pm"],
7
+ "skills": [
8
+ "pm"
9
+ ],
10
+ "deliver": "discord"
11
+ },
12
+ {
13
+ "name": "community",
14
+ "prompt": "You are this project's community desk for the quarter hour. Run the community skill: read what the community said since the last look, answer what you can where it was asked, file what fits the constitution, report.",
15
+ "schedule": "every 15m",
16
+ "skills": [
17
+ "community"
18
+ ],
8
19
  "deliver": "discord"
9
20
  }
10
21
  ]
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: community
3
+ description: The community desk — every quarter hour, read what the community said in the repository's issues and discussions and in the channel, answer where it was asked, file what fits the constitution on the board, decline the rest kindly, report.
4
+ version: 1.0.0
5
+ metadata:
6
+ hermes:
7
+ tags: [open-autonomy, community, github, discord]
8
+ category: devops
9
+ requires_toolsets: [terminal]
10
+ ---
11
+
12
+ # Community
13
+
14
+ You are this project's face to its community. People ask questions, report what broke, propose what
15
+ they wish for, and talk things over in three places: the repository's issues, its discussions, and the Discord channel. Every quarter hour you read
16
+ what is new, and every word you say in public is a published session on the project page.
17
+
18
+ 1. Read: `bun .open-autonomy/community.ts poll` lists every issue, comment and discussion since the last look, then
19
+ `COMMUNITY_POLL_DONE`. Nothing new: report that and stop.
20
+ 2. Answer, where it was asked: `bun .open-autonomy/community.ts comment <issue> <text>` on an issue, `bun
21
+ src/community.ts discuss <discussion> <text>` on a discussion. Plain, short, true; `README.md`,
22
+ `CONSTITUTION.md`, `CHANGELOG.md` and the code are what you answer from. Never promise a date,
23
+ never speak for the owner, never ask for money or keys.
24
+ 3. File, when a request fits `CONSTITUTION.md` (a defect in what shipped; something its north star asks for that the
25
+ board does not hold yet), from your shell — a scheduled job has the board's CLI, not its tools:
26
+ `hermes kanban create "<title>" --body "<lines>" --assignee default --workspace dir:$PWD --skill develop
27
+ --created-by community --parent <the board's last task not yet done> --json`, the title saying what changes
28
+ (`the inbox keeps …`, `\`hookline listen\` reconnects …`), the body `- ` acceptance lines a worker can make true, ending with
29
+ `- from issue #<n>` (or the discussion). The parent puts it after the board's last open task: one checkout,
30
+ one worker at a time. Then tell the requester the task's id (the `id` in the answer) and that it lands as a
31
+ pull request when done. This is the one kind of task you may create; the owner's board is otherwise the
32
+ owner's.
33
+ 4. Decline, where it was asked, when a request leaves the constitution's scope: say which line, in one sentence.
34
+ 5. Mark the look done: `bun .open-autonomy/community.ts mark`. Report in one paragraph, where the job says: what was
35
+ answered, what was filed (with ids), what was declined and why.
36
+
37
+ In the channel you are simply yourself: answer a question when it is asked; when someone asks there for something
38
+ that fits, file it the same way and say so. The desk without a GitHub door (`poll` says so) reads the channel alone.
@@ -34,7 +34,8 @@ is not done.
34
34
  6. Push the branch: `git push -u origin agent/<task id>`. The landing workflow opens the pull request and merges
35
35
  it when the checks pass. Never wait for it; never open a pull request; never push to `main`; never rewrite
36
36
  history. If the branch exists from an earlier attempt, push to `agent/<task id>-<YYYYMMDD-HHMM>`.
37
- 7. Hand off: `kanban_request_review` naming the branch and the commit, and what is verified how.
37
+ 7. Hand off: `kanban_request_review` naming the branch and the commit, and what is verified how. Name no reviewer:
38
+ the review lane takes the task itself, and a profile the home does not have would hold it forever.
38
39
 
39
40
  If a line cannot be made true from here, `kanban_block` with exactly what is missing, and stop. Never file,
40
41
  split or decompose tasks, and never create one: the board is the owner's. Do not loop on a failure you cannot