@etiquekit/etq 0.0.1 → 1.0.1

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.
Files changed (83) hide show
  1. package/AGENTS.md +28 -0
  2. package/QuickStart.md +94 -0
  3. package/README.md +77 -9
  4. package/bin/etiquette +1149 -0
  5. package/bin/etiquette-core +313 -0
  6. package/docs/ARCHITECTURE.md +100 -0
  7. package/docs/CODEX_CLIENT_COMPATIBILITY.md +83 -0
  8. package/docs/CONCEPTS.md +128 -0
  9. package/docs/CONCEPT_STATUS.md +67 -0
  10. package/docs/CORE_PROFILE.md +70 -0
  11. package/docs/LANE_PROVISIONING.md +157 -0
  12. package/docs/README.md +29 -0
  13. package/docs/RELEASE_SURFACE_AUDIT.md +156 -0
  14. package/docs/SEAT_DISCIPLINE.md +68 -0
  15. package/docs/SEAT_PROVISIONING.md +100 -0
  16. package/docs/TEAM_HANDOFF.md +190 -0
  17. package/docs/WORKTREE_QOL.md +92 -0
  18. package/docs/contracts/ledger-entry/README.md +83 -0
  19. package/docs/contracts/ledger-entry/ledger-entry.v0.1.md +116 -0
  20. package/docs/contracts/ledger-entry/ledger-entry.v0.2.md +90 -0
  21. package/docs/contracts/ledger-entry/ledger-entry.v0.md +123 -0
  22. package/docs/contracts/ledger-entry/ledger-query-cli.v0.1.md +64 -0
  23. package/package.json +42 -11
  24. package/scripts/install.sh +144 -0
  25. package/scripts/uninstall.sh +58 -0
  26. package/templates/etiquette-vanilla-v0/README.md +69 -0
  27. package/templates/etiquette-vanilla-v0/bin/stamp-vanilla +33 -0
  28. package/templates/etiquette-vanilla-v0/source/control-seat/.llm-substrate/HANDOFF.md +22 -0
  29. package/templates/etiquette-vanilla-v0/source/control-seat/README.md +16 -0
  30. package/templates/etiquette-vanilla-v0/source/control-seat/bin/_lib/http.sh +121 -0
  31. package/templates/etiquette-vanilla-v0/source/control-seat/bin/_lib/sanitize.sh +191 -0
  32. package/templates/etiquette-vanilla-v0/source/control-seat/bin/_lib/state.sh +67 -0
  33. package/templates/etiquette-vanilla-v0/source/control-seat/bin/access-assurance-check +55 -0
  34. package/templates/etiquette-vanilla-v0/source/control-seat/bin/compact-local-events +71 -0
  35. package/templates/etiquette-vanilla-v0/source/control-seat/bin/hooks/post-tool-use +64 -0
  36. package/templates/etiquette-vanilla-v0/source/control-seat/bin/hooks/session-end +64 -0
  37. package/templates/etiquette-vanilla-v0/source/control-seat/bin/hooks/session-start +64 -0
  38. package/templates/etiquette-vanilla-v0/source/control-seat/bin/hooks/stop +64 -0
  39. package/templates/etiquette-vanilla-v0/source/control-seat/bin/hooks/user-prompt-submit +64 -0
  40. package/templates/etiquette-vanilla-v0/source/control-seat/bin/local-event +53 -0
  41. package/templates/etiquette-vanilla-v0/source/control-seat/bin/post-receipt +143 -0
  42. package/templates/etiquette-vanilla-v0/source/control-seat/bin/post-telemetry +112 -0
  43. package/templates/etiquette-vanilla-v0/source/control-seat/bin/seat-consume-pass +115 -0
  44. package/templates/etiquette-vanilla-v0/source/control-seat/bin/seat-doctor +20 -0
  45. package/templates/etiquette-vanilla-v0/source/control-seat/bin/seat-init +86 -0
  46. package/templates/etiquette-vanilla-v0/source/control-seat/bin/seat-post-readiness +171 -0
  47. package/templates/etiquette-vanilla-v0/source/control-seat/bin/session-init +132 -0
  48. package/templates/etiquette-vanilla-v0/source/control-seat/docs/work/access/ACCESS-ASSURANCE.md +39 -0
  49. package/templates/etiquette-vanilla-v0/source/control-seat/docs/work/access/workspace-secure-profile.v0.json +53 -0
  50. package/templates/etiquette-vanilla-v0/source/control-seat/docs/work/ledger/LEDGER.md +7 -0
  51. package/templates/etiquette-vanilla-v0/source/control-seat/docs/work/playbacks/PLAYBACK_TEMPLATE.md +31 -0
  52. package/templates/etiquette-vanilla-v0/source/control-seat/docs/work/receipts/.gitkeep +1 -0
  53. package/templates/etiquette-vanilla-v0/source/control-seat/docs/work/tasks/CONTROL-001.yaml +13 -0
  54. package/templates/etiquette-vanilla-v0/source/etiquette-kernel/README.md +6 -0
  55. package/templates/etiquette-vanilla-v0/source/etiquette-kernel/bin/kernel-doctor +11 -0
  56. package/templates/etiquette-vanilla-v0/source/etiquette-kernel/contracts/playback.v0.md +37 -0
  57. package/templates/etiquette-vanilla-v0/source/etiquette-kernel/contracts/receipt.v0.md +25 -0
  58. package/templates/etiquette-vanilla-v0/source/etiquette-kernel/contracts/seat-readiness.v0.md +22 -0
  59. package/templates/etiquette-vanilla-v0/source/etiquette-kernel/contracts/task.v0.md +18 -0
  60. package/templates/etiquette-vanilla-v0/validate-vanilla.sh +43 -0
  61. package/templates/github-actions/README.md +30 -0
  62. package/templates/github-actions/w1-webhook-wake-canary.yml +111 -0
  63. package/templates/hosted-receiver/README.md +41 -0
  64. package/templates/hosted-receiver/w1-github-webhook-receiver.mjs +129 -0
  65. package/templates/seat-packs-v0/README.md +72 -0
  66. package/templates/seat-packs-v0/bin/provision-seat +70 -0
  67. package/templates/seat-packs-v0/source/claude-code-seat/.llm-substrate/HANDOFF.md +24 -0
  68. package/templates/seat-packs-v0/source/claude-code-seat/README.md +19 -0
  69. package/templates/seat-packs-v0/source/claude-code-seat/bin/seat-doctor +8 -0
  70. package/templates/seat-packs-v0/source/codex-seat/.llm-substrate/HANDOFF.md +25 -0
  71. package/templates/seat-packs-v0/source/codex-seat/README.md +22 -0
  72. package/templates/seat-packs-v0/source/codex-seat/bin/seat-doctor +8 -0
  73. package/templates/seat-packs-v0/source/gemini-seat/.llm-substrate/HANDOFF.md +23 -0
  74. package/templates/seat-packs-v0/source/gemini-seat/README.md +19 -0
  75. package/templates/seat-packs-v0/source/gemini-seat/bin/seat-doctor +8 -0
  76. package/templates/seat-packs-v0/source/ollama-seat/.llm-substrate/HANDOFF.md +24 -0
  77. package/templates/seat-packs-v0/source/ollama-seat/README.md +19 -0
  78. package/templates/seat-packs-v0/source/ollama-seat/bin/seat-doctor +8 -0
  79. package/templates/seat-packs-v0/source/openrouter-seat/.llm-substrate/HANDOFF.md +24 -0
  80. package/templates/seat-packs-v0/source/openrouter-seat/README.md +19 -0
  81. package/templates/seat-packs-v0/source/openrouter-seat/bin/seat-doctor +8 -0
  82. package/templates/seat-packs-v0/validate-seat-packs.sh +27 -0
  83. package/bin/etq +0 -4
@@ -0,0 +1,11 @@
1
+ #!/bin/sh
2
+ set -eu
3
+
4
+ ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
5
+
6
+ test -f "$ROOT/contracts/receipt.v0.md"
7
+ test -f "$ROOT/contracts/playback.v0.md"
8
+ test -f "$ROOT/contracts/seat-readiness.v0.md"
9
+ test -f "$ROOT/contracts/task.v0.md"
10
+
11
+ echo "etiquette-kernel: ok"
@@ -0,0 +1,37 @@
1
+ # Playback V0
2
+
3
+ A playback is a manual review of evidence after meaningful workflow change.
4
+ It interprets receipts; it does not replace receipts.
5
+
6
+ ```yaml
7
+ playback_id:
8
+ timeframe:
9
+ scope:
10
+ operator_principal:
11
+ execution_seat:
12
+ inputs:
13
+ receipts: []
14
+ ledger_refs: []
15
+ commit_refs: []
16
+ decisions:
17
+ - decision:
18
+ evidence_refs: []
19
+ lessons:
20
+ - lesson:
21
+ evidence_refs: []
22
+ deferred:
23
+ - item:
24
+ reason:
25
+ proposed_actions:
26
+ - action:
27
+ type: no_change | docs_patch | memory_save | controlled_drill
28
+ evidence_refs: []
29
+ ```
30
+
31
+ Rules:
32
+
33
+ - Receipts are evidence.
34
+ - Playback interprets evidence.
35
+ - Controlled drills require one operator approval per drill.
36
+ - Advisory sources may be referenced, but they are not proof.
37
+ - Automation waits until manual playback shows a repeated gap.
@@ -0,0 +1,25 @@
1
+ # Receipt V0
2
+
3
+ Every meaningful task returns a compact receipt.
4
+
5
+ ```yaml
6
+ task_id:
7
+ operator_principal:
8
+ execution_seat:
9
+ status: success | partial | blocked | failed
10
+ sources_read: []
11
+ files_changed: []
12
+ validation: []
13
+ risks: []
14
+ next_owner:
15
+ # Optional when compacting local events into a receipt:
16
+ compaction:
17
+ scope: local-only
18
+ event_log:
19
+ event_count:
20
+ first_seq:
21
+ last_seq:
22
+ last_action:
23
+ ```
24
+
25
+ Receipts are durable. Chat is not canonical evidence.
@@ -0,0 +1,22 @@
1
+ # Seat Readiness V0
2
+
3
+ Each seat reports capability before receiving work.
4
+
5
+ ```yaml
6
+ seat:
7
+ seat_type: control_seat | execution_seat | review_seat | kernel_maintainer | seat_maintainer
8
+ execution_surface:
9
+ declared_by: operator_principal | config
10
+ capability_source: config | local_validation
11
+ operational_status: operational | degraded | blocked | offline
12
+ worker_capacity:
13
+ parallel_support: true | false
14
+ supported_strategies: []
15
+ blocked_strategies: []
16
+ validation: []
17
+ constraints: []
18
+ last_checked_at:
19
+ ```
20
+
21
+ Unsupported strategies hard-block only when `strategy_policy: required`.
22
+ Agent confidence is not proof of capability. Capability is operator/config declared and backed by local validation.
@@ -0,0 +1,18 @@
1
+ # Task V0
2
+
3
+ A task says what to do, what may change, and how to validate completion.
4
+
5
+ ```yaml
6
+ task_id:
7
+ owner:
8
+ mode: read-only | docs-only | implementation | review
9
+ repo:
10
+ allowed_writes: []
11
+ forbidden_sources: []
12
+ validation: []
13
+ stop_conditions: []
14
+ expected_return: []
15
+ ```
16
+
17
+ Tasks should be small enough that a receipt can prove what happened.
18
+
@@ -0,0 +1,43 @@
1
+ #!/bin/sh
2
+ set -eu
3
+
4
+ ROOT=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
5
+
6
+ test -f "$ROOT/README.md"
7
+ test -d "$ROOT/source/etiquette-kernel"
8
+ test -d "$ROOT/source/control-seat"
9
+ grep -Fq ".etiquette/events/*.ndjson" "$ROOT/source/control-seat/.gitignore"
10
+ grep -q '"schema": "workspace-secure-profile.v0"' "$ROOT/source/control-seat/docs/work/access/workspace-secure-profile.v0.json"
11
+ grep -q '"mode": "open"' "$ROOT/source/control-seat/docs/work/access/workspace-secure-profile.v0.json"
12
+ grep -q "enterprise-reactor" "$ROOT/source/control-seat/docs/work/access/ACCESS-ASSURANCE.md"
13
+ grep -q "Controlled drills require one operator approval per drill" "$ROOT/source/etiquette-kernel/contracts/playback.v0.md"
14
+ grep -q "batch_approval_allowed: false" "$ROOT/source/control-seat/docs/work/playbacks/PLAYBACK_TEMPLATE.md"
15
+
16
+ sh "$ROOT/source/etiquette-kernel/bin/kernel-doctor"
17
+ sh "$ROOT/source/control-seat/bin/seat-doctor"
18
+ sh "$ROOT/source/control-seat/bin/access-assurance-check" | grep -q "mode=open"
19
+
20
+ tmp_root=$(mktemp -d "${TMPDIR:-/tmp}/etiquette-vanilla-compact.XXXXXX")
21
+ trap 'rm -rf "$tmp_root"' EXIT
22
+
23
+ cp -R "$ROOT/source/control-seat/." "$tmp_root/control-seat/"
24
+ sh "$tmp_root/control-seat/bin/seat-init" --execution-seat control-seat >/dev/null
25
+ sh "$tmp_root/control-seat/bin/access-assurance-check" | grep -q "mode=open"
26
+ sh "$tmp_root/control-seat/bin/session-init" --task-id SESSION-INIT --operator local-operator >/tmp/session-init.out
27
+ test -f "$tmp_root/control-seat/.etiquette/state/session.json"
28
+ grep -q "session_opened" "$tmp_root/control-seat/docs/work/ledger/LEDGER.md"
29
+ sh "$tmp_root/control-seat/bin/local-event" CONTROL-001 control-seat started "started local proof"
30
+ sh "$tmp_root/control-seat/bin/local-event" CONTROL-001 control-seat validated "ran seat doctor"
31
+ sh "$tmp_root/control-seat/bin/local-event" CONTROL-001 control-seat completed "ready to compact"
32
+ sh "$tmp_root/control-seat/bin/compact-local-events" CONTROL-001 success
33
+
34
+ receipt="$tmp_root/control-seat/docs/work/receipts/CONTROL-001-receipt.yaml"
35
+ test -f "$receipt"
36
+ grep -q "scope: local-only" "$receipt"
37
+ grep -q "operator_principal: local-operator" "$receipt"
38
+ grep -q "execution_seat: control-seat" "$receipt"
39
+ grep -q "event_count: 3" "$receipt"
40
+ grep -q "first_seq: 2" "$receipt"
41
+ grep -q "last_seq: 4" "$receipt"
42
+
43
+ echo "etiquette vanilla: ok"
@@ -0,0 +1,30 @@
1
+ # GitHub Actions Templates
2
+
3
+ These templates are opt-in canaries. Copy one into a disposable repository
4
+ before using it on a production repo.
5
+
6
+ ## W1 Webhook Wake Canary
7
+
8
+ `w1-webhook-wake-canary.yml` proves the W1 receiver loop inside GitHub Actions:
9
+
10
+ 1. read the GitHub push event payload;
11
+ 2. sign it with a canary HMAC secret;
12
+ 3. call `etiquette notify webhook github`;
13
+ 4. verify the emitted wake points at the pushed commit;
14
+ 5. fetch the commit from Git before treating the wake as useful.
15
+
16
+ Required secret:
17
+
18
+ - `ETIQUETTE_GITHUB_WEBHOOK_SECRET`: canary HMAC secret used only for this proof.
19
+
20
+ Choose one CLI source:
21
+
22
+ - set repository variable `ETIQUETTE_CLI` to an installed command such as
23
+ `etiquette`; or
24
+ - set repository variable `ETIQUETTE_SOURCE_REPOSITORY` and secret
25
+ `ETIQUETTE_REPO_TOKEN` so the workflow can check out the Etiquette source
26
+ read-only.
27
+
28
+ Boundary: the workflow emits a pointer-only wake artifact and step summary. It
29
+ does not install a public webhook, call AWS/SNS/SQS, post to Slack, run remote
30
+ agents, merge, close, publish, or grant authority.
@@ -0,0 +1,111 @@
1
+ name: Etiquette W1 webhook wake canary
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - "**"
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ env:
12
+ ETIQUETTE_CLI: ${{ vars.ETIQUETTE_CLI }}
13
+
14
+ jobs:
15
+ w1-webhook-wake:
16
+ name: Signed push payload to pointer-only wake
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - name: Checkout canary repo
20
+ uses: actions/checkout@v4
21
+ with:
22
+ fetch-depth: 1
23
+
24
+ - name: Setup Bun
25
+ if: ${{ env.ETIQUETTE_CLI == '' }}
26
+ uses: oven-sh/setup-bun@v2
27
+
28
+ - name: Check Etiquette source configuration
29
+ if: ${{ env.ETIQUETTE_CLI == '' }}
30
+ env:
31
+ ETIQUETTE_SOURCE_REPOSITORY: ${{ vars.ETIQUETTE_SOURCE_REPOSITORY }}
32
+ run: |
33
+ test -n "${ETIQUETTE_SOURCE_REPOSITORY:-}" || {
34
+ echo "Set ETIQUETTE_CLI, or set ETIQUETTE_SOURCE_REPOSITORY plus ETIQUETTE_REPO_TOKEN."
35
+ exit 1
36
+ }
37
+
38
+ - name: Checkout Etiquette source
39
+ if: ${{ env.ETIQUETTE_CLI == '' }}
40
+ uses: actions/checkout@v4
41
+ with:
42
+ repository: ${{ vars.ETIQUETTE_SOURCE_REPOSITORY }}
43
+ ref: main
44
+ path: .etq/etiquette
45
+ token: ${{ secrets.ETIQUETTE_REPO_TOKEN }}
46
+
47
+ - name: Install Etiquette source dependencies
48
+ if: ${{ env.ETIQUETTE_CLI == '' }}
49
+ working-directory: .etq/etiquette
50
+ run: bun install --frozen-lockfile
51
+
52
+ - name: Verify signed wake and Git ref
53
+ env:
54
+ ETIQUETTE_GITHUB_WEBHOOK_SECRET: ${{ secrets.ETIQUETTE_GITHUB_WEBHOOK_SECRET }}
55
+ run: |
56
+ set -eu
57
+ test -n "${ETIQUETTE_GITHUB_WEBHOOK_SECRET:-}" || {
58
+ echo "ETIQUETTE_GITHUB_WEBHOOK_SECRET is required."
59
+ exit 1
60
+ }
61
+
62
+ ETQ_CMD="${ETIQUETTE_CLI:-bun $GITHUB_WORKSPACE/.etq/etiquette/packages/control/src/cli.ts}"
63
+ SIGNATURE="$(node <<'NODE'
64
+ const crypto = require('node:crypto');
65
+ const fs = require('node:fs');
66
+ const payload = fs.readFileSync(process.env.GITHUB_EVENT_PATH);
67
+ const secret = process.env.ETIQUETTE_GITHUB_WEBHOOK_SECRET;
68
+ process.stdout.write(`sha256=${crypto.createHmac('sha256', secret).update(payload).digest('hex')}`);
69
+ NODE
70
+ )"
71
+
72
+ ${ETQ_CMD} notify webhook github \
73
+ --payload "$GITHUB_EVENT_PATH" \
74
+ --delivery "$GITHUB_RUN_ID" \
75
+ --event push \
76
+ --signature-256 "$SIGNATURE" \
77
+ --json > w1-wake.json
78
+
79
+ node <<'NODE'
80
+ const fs = require('node:fs');
81
+ const wake = JSON.parse(fs.readFileSync('w1-wake.json', 'utf8'));
82
+ const sha = process.env.GITHUB_SHA;
83
+ if (wake.schema !== 'github-webhook-wake.v0') throw new Error('unexpected wake schema');
84
+ if (wake.after !== sha) throw new Error(`wake.after ${wake.after} did not match GITHUB_SHA ${sha}`);
85
+ if (wake.transport.webhook_payload_trusted_as_truth !== false) throw new Error('payload truth boundary drifted');
86
+ if (wake.transport.receiver_must_fetch_and_verify !== true) throw new Error('fetch/verify boundary missing');
87
+ if (wake.payload_boundary.pointers_only !== true) throw new Error('pointer-only boundary drifted');
88
+ if (wake.authority_boundary.notification_is_authority !== false) throw new Error('authority boundary drifted');
89
+ NODE
90
+
91
+ git fetch --no-tags --depth=1 origin "$GITHUB_SHA"
92
+ test "$(git rev-parse FETCH_HEAD)" = "$GITHUB_SHA"
93
+ git cat-file -e "$GITHUB_SHA^{commit}"
94
+
95
+ {
96
+ echo "### Etiquette W1 wake canary"
97
+ echo ""
98
+ echo "- schema: github-webhook-wake.v0"
99
+ echo "- repository: $(node -e "const w=require('./w1-wake.json'); console.log(w.repository.full_name)")"
100
+ echo "- ref: $(node -e "const w=require('./w1-wake.json'); console.log(w.ref)")"
101
+ echo "- after: $(node -e "const w=require('./w1-wake.json'); console.log(w.after)")"
102
+ echo "- signature_verified: true"
103
+ echo "- notification_is_authority: false"
104
+ echo "- next_action: receiver_fetch_and_verify_git_ref"
105
+ } >> "$GITHUB_STEP_SUMMARY"
106
+
107
+ - name: Upload pointer-only wake artifact
108
+ uses: actions/upload-artifact@v4
109
+ with:
110
+ name: etiquette-w1-wake
111
+ path: w1-wake.json
@@ -0,0 +1,41 @@
1
+ # Hosted Receiver Templates
2
+
3
+ Opt-in templates for teams that want to expose Etiquette's W1 GitHub webhook
4
+ receiver behind their own hosting layer.
5
+
6
+ ## W1 GitHub Receiver
7
+
8
+ `w1-github-webhook-receiver.mjs` is a small Node HTTP server:
9
+
10
+ - `GET /health` returns a readiness probe.
11
+ - `POST /github` accepts GitHub `ping` and `push` webhook deliveries.
12
+ - `ping` returns `200` only after the delivery is signature-verified.
13
+ - `push` delegates to `etiquette notify webhook github`.
14
+ - raw payloads are written only to a temporary file for the CLI call, then
15
+ removed.
16
+ - emitted records stay pointer-only and authority-false.
17
+
18
+ Run it with:
19
+
20
+ ```sh
21
+ export ETIQUETTE_GITHUB_WEBHOOK_SECRET='<webhook-secret>'
22
+ export ETIQUETTE_CLI='etiquette'
23
+ node w1-github-webhook-receiver.mjs
24
+ ```
25
+
26
+ Optional:
27
+
28
+ ```sh
29
+ export PORT=8080
30
+ export ETIQUETTE_WAKE_OUT_DIR=/var/lib/etiquette/wake
31
+ ```
32
+
33
+ `ETIQUETTE_WAKE_OUT_DIR` stores only the verified pointer output, never the raw
34
+ webhook body.
35
+
36
+ ## Boundary
37
+
38
+ This template does not create a public endpoint, register repository webhooks,
39
+ provision cloud infrastructure, start remote execution, or grant authority. Host
40
+ selection, webhook registration, secret rotation, and retention policy remain
41
+ operator decisions.
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env node
2
+ import { createServer } from 'node:http';
3
+ import { randomUUID } from 'node:crypto';
4
+ import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
5
+ import { tmpdir } from 'node:os';
6
+ import { join } from 'node:path';
7
+ import { spawnSync } from 'node:child_process';
8
+
9
+ const port = Number(process.env.PORT || '8080');
10
+ const etiquetteCli = process.env.ETIQUETTE_CLI || 'etiquette';
11
+ const secret = process.env.ETIQUETTE_GITHUB_WEBHOOK_SECRET;
12
+ const outDir = process.env.ETIQUETTE_WAKE_OUT_DIR || '';
13
+
14
+ if (!secret) throw new Error('ETIQUETTE_GITHUB_WEBHOOK_SECRET is required');
15
+
16
+ function readRaw(req) {
17
+ return new Promise((resolve, reject) => {
18
+ const chunks = [];
19
+ req.on('data', (chunk) => chunks.push(chunk));
20
+ req.on('end', () => resolve(Buffer.concat(chunks)));
21
+ req.on('error', reject);
22
+ });
23
+ }
24
+
25
+ function writeJson(res, status, value) {
26
+ res.writeHead(status, {
27
+ 'content-type': 'application/json',
28
+ 'cache-control': 'no-store',
29
+ });
30
+ res.end(`${JSON.stringify(value, null, 2)}\n`);
31
+ }
32
+
33
+ function publicRecord(record) {
34
+ return {
35
+ ok: true,
36
+ schema: record.schema,
37
+ event: record.event,
38
+ delivery_id: record.delivery_id,
39
+ repository: record.repository.full_name,
40
+ ref: record.ref || null,
41
+ after: record.after || null,
42
+ signature_verified: record.transport.signature_verified,
43
+ pointers_only: record.payload_boundary.pointers_only,
44
+ notification_is_authority: record.authority_boundary.notification_is_authority,
45
+ next_action: record.next_action,
46
+ };
47
+ }
48
+
49
+ async function verifyDelivery(req, event, delivery, signature) {
50
+ const raw = await readRaw(req);
51
+ const payloadPath = join(tmpdir(), `etiquette-webhook-${delivery}-${randomUUID()}.json`);
52
+ writeFileSync(payloadPath, raw);
53
+ try {
54
+ const result = spawnSync(etiquetteCli, [
55
+ 'notify',
56
+ 'webhook',
57
+ 'github',
58
+ '--payload',
59
+ payloadPath,
60
+ '--delivery',
61
+ delivery,
62
+ '--event',
63
+ event,
64
+ '--signature-256',
65
+ signature,
66
+ '--json',
67
+ ], {
68
+ encoding: 'utf8',
69
+ env: {
70
+ ...process.env,
71
+ ETIQUETTE_GITHUB_WEBHOOK_SECRET: secret,
72
+ },
73
+ });
74
+ if (result.status !== 0) {
75
+ return {
76
+ ok: false,
77
+ status: 401,
78
+ body: {
79
+ ok: false,
80
+ error: 'signature_or_payload_rejected',
81
+ detail: result.stderr.trim().slice(0, 200),
82
+ },
83
+ };
84
+ }
85
+ const record = JSON.parse(result.stdout);
86
+ if (outDir) {
87
+ mkdirSync(outDir, { recursive: true });
88
+ writeFileSync(join(outDir, 'latest.json'), `${JSON.stringify(record, null, 2)}\n`);
89
+ writeFileSync(join(outDir, `${delivery}.json`), `${JSON.stringify(record, null, 2)}\n`);
90
+ }
91
+ return { ok: true, status: 200, body: publicRecord(record) };
92
+ } finally {
93
+ rmSync(payloadPath, { force: true });
94
+ }
95
+ }
96
+
97
+ const server = createServer(async (req, res) => {
98
+ try {
99
+ if (req.method === 'GET' && req.url === '/health') {
100
+ writeJson(res, 200, { ok: true, authority: false });
101
+ return;
102
+ }
103
+ if (req.method !== 'POST' || req.url !== '/github') {
104
+ writeJson(res, 404, { ok: false, error: 'not_found' });
105
+ return;
106
+ }
107
+
108
+ const event = String(req.headers['x-github-event'] || '');
109
+ const delivery = String(req.headers['x-github-delivery'] || randomUUID());
110
+ const signature = String(req.headers['x-hub-signature-256'] || '');
111
+ if (event !== 'push' && event !== 'ping') {
112
+ writeJson(res, 400, { ok: false, error: 'unsupported_event', event });
113
+ return;
114
+ }
115
+ if (!signature.startsWith('sha256=')) {
116
+ writeJson(res, 401, { ok: false, error: 'missing_signature' });
117
+ return;
118
+ }
119
+
120
+ const result = await verifyDelivery(req, event, delivery, signature);
121
+ writeJson(res, result.status, result.body);
122
+ } catch (error) {
123
+ writeJson(res, 500, { ok: false, error: String(error?.message || error) });
124
+ }
125
+ });
126
+
127
+ server.listen(port, '0.0.0.0', () => {
128
+ console.log(`etiquette_w1_receiver: listening on ${port}`);
129
+ });
@@ -0,0 +1,72 @@
1
+ # Seat Packs V0
2
+
3
+ Optional runtime-specific seat templates for Etiquette.
4
+
5
+ Vanilla Etiquette starts with one generic `control-seat`. Use these templates only after the control seat decides a runtime-specific seat is useful.
6
+
7
+ ## Available Seat Templates
8
+
9
+ ```yaml
10
+ codex:
11
+ execution_surface: codex
12
+ template: source/codex-seat
13
+
14
+ claude-code:
15
+ execution_surface: claude_code
16
+ template: source/claude-code-seat
17
+
18
+ gemini:
19
+ execution_surface: gemini_cli
20
+ template: source/gemini-seat
21
+
22
+ openrouter:
23
+ execution_surface: openrouter
24
+ template: source/openrouter-seat
25
+
26
+ ollama:
27
+ execution_surface: ollama
28
+ template: source/ollama-seat
29
+ ```
30
+
31
+ ## Provision Explicitly
32
+
33
+ ```sh
34
+ sh bin/provision-seat codex /tmp/dev-codex-seat
35
+ sh bin/provision-seat claude-code /tmp/dev-claude-code-seat
36
+ sh bin/provision-seat gemini /tmp/dev-gemini-seat
37
+ sh bin/provision-seat openrouter /tmp/dev-openrouter-seat
38
+ sh bin/provision-seat ollama /tmp/dev-ollama-seat
39
+ ```
40
+
41
+ Seat packs do not bundle monitor skills. For bounded wake checks, every seat
42
+ uses the portable core command `etiquette notify poll --seat <id> --after-seq <n>`,
43
+ which is read-only, does not grant authority, and does not start a daemon.
44
+
45
+ ## Provision By Detection
46
+
47
+ ```sh
48
+ sh bin/provision-seat auto /tmp/dev-seat
49
+ ```
50
+
51
+ Auto-detect succeeds only when exactly one supported runtime is obvious. If several runtimes are available, choose explicitly.
52
+
53
+ ## Head-Start Discipline
54
+
55
+ Runtime seat packs are not authority. After provisioning a seat into a project,
56
+ the seat inherits the target project's ledger and task envelopes. Read the
57
+ project head-start surfaces before pickup:
58
+
59
+ - `AGENTS.md`
60
+ - `docs/work/BOARD.md`
61
+ - `docs/work/runbooks/SESSION_PICKUP.md`
62
+ - `docs/work/manuals/SEAT_OPERATING_MANUAL.md`
63
+ - `docs/work/cookbooks/ADD_A_LANE.md` when routing work
64
+
65
+ Use `$etiquette-seat-lifecycle` before pickup after stale context, resume,
66
+ runner/model/profile swaps, or workflow migration.
67
+
68
+ ## Validate Catalog
69
+
70
+ ```sh
71
+ sh validate-seat-packs.sh
72
+ ```
@@ -0,0 +1,70 @@
1
+ #!/bin/sh
2
+ set -eu
3
+
4
+ ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
5
+
6
+ usage() {
7
+ echo "usage: sh bin/provision-seat <codex|claude-code|gemini|openrouter|ollama|auto> <target-dir>" >&2
8
+ }
9
+
10
+ if [ "$#" -ne 2 ]; then
11
+ usage
12
+ exit 2
13
+ fi
14
+
15
+ seat=$1
16
+ target=$2
17
+
18
+ detect() {
19
+ found=""
20
+ command -v codex >/dev/null 2>&1 && found="$found codex"
21
+ command -v claude >/dev/null 2>&1 && found="$found claude-code"
22
+ command -v gemini >/dev/null 2>&1 && found="$found gemini"
23
+ command -v ollama >/dev/null 2>&1 && found="$found ollama"
24
+ [ -n "${OPENROUTER_API_KEY:-}" ] && found="$found openrouter"
25
+
26
+ count=$(set -- $found; echo "$#")
27
+ if [ "$count" -eq 1 ]; then
28
+ set -- $found
29
+ echo "$1"
30
+ return
31
+ fi
32
+ if [ "$count" -eq 0 ]; then
33
+ echo "auto-detect found no supported runtime; choose explicit seat type" >&2
34
+ exit 2
35
+ fi
36
+ echo "auto-detect found multiple runtimes:$found; choose explicit seat type" >&2
37
+ exit 2
38
+ }
39
+
40
+ if [ "$seat" = "auto" ]; then
41
+ seat=$(detect)
42
+ fi
43
+
44
+ case "$seat" in
45
+ codex) src="$ROOT/source/codex-seat" ;;
46
+ claude-code) src="$ROOT/source/claude-code-seat" ;;
47
+ gemini) src="$ROOT/source/gemini-seat" ;;
48
+ openrouter) src="$ROOT/source/openrouter-seat" ;;
49
+ ollama) src="$ROOT/source/ollama-seat" ;;
50
+ *) usage; exit 2 ;;
51
+ esac
52
+
53
+ if [ -e "$target" ]; then
54
+ echo "target already exists: $target" >&2
55
+ exit 2
56
+ fi
57
+
58
+ cp -R "$src" "$target"
59
+
60
+ if command -v git >/dev/null 2>&1; then
61
+ git -C "$target" init -q
62
+ git -C "$target" add .
63
+ git -C "$target" \
64
+ -c user.name="Etiquette Seat" \
65
+ -c user.email="etiquette-seat@example.invalid" \
66
+ commit -q -m "Initial $seat seat scaffold"
67
+ fi
68
+
69
+ echo "provisioned $seat seat at $target"
70
+
@@ -0,0 +1,24 @@
1
+ # Claude Code Seat Handoff
2
+
3
+ You are an Etiquette Claude Code seat.
4
+
5
+ ## Startup
6
+
7
+ 1. Run `sh bin/seat-doctor`.
8
+ 2. Read the target project's head-start surfaces.
9
+ 3. Report readiness before claiming work.
10
+
11
+ ## Default Capability
12
+
13
+ ```yaml
14
+ seat_type: execution_seat | review_seat
15
+ execution_surface: claude_code
16
+ supported_strategies:
17
+ - solo
18
+ - local_adversarial_tango
19
+ - telescope
20
+ blocked_strategies:
21
+ - required_cross_runtime_without_backplane
22
+ ```
23
+
24
+ Do not clone product repos until assigned.
@@ -0,0 +1,19 @@
1
+ # Claude Code Seat
2
+
3
+ Optional Etiquette seat template for a Claude Code runtime.
4
+
5
+ Use this only after the control seat grants a Claude Code execution or review seat.
6
+
7
+ ## Head-Start
8
+
9
+ Before pickup in the target project, read:
10
+
11
+ 1. `AGENTS.md`
12
+ 2. `docs/work/BOARD.md`
13
+ 3. `docs/work/runbooks/SESSION_PICKUP.md`
14
+ 4. `docs/work/manuals/SEAT_OPERATING_MANUAL.md`
15
+ 5. `docs/work/cookbooks/ADD_A_LANE.md` when routing new work
16
+
17
+ Use `$etiquette-seat-lifecycle` before pickup after stale context, resume,
18
+ runner/model/profile swaps, or workflow migration. Hooks and readiness are
19
+ evidence only; the task envelope is the work authority.
@@ -0,0 +1,8 @@
1
+ #!/bin/sh
2
+ set -eu
3
+ ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
4
+ test -f "$ROOT/.llm-substrate/HANDOFF.md"
5
+ if [ "${ETIQUETTE_REQUIRE_RUNTIME:-0}" = "1" ]; then
6
+ command -v claude >/dev/null 2>&1
7
+ fi
8
+ echo "claude-code-seat: ok"