@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,115 @@
1
+ #!/bin/sh
2
+ # seat-consume-pass: redeem a setup-pass against the hosted plane and cache
3
+ # the returned envelope into $STATE_DIR/workspace.json.
4
+ #
5
+ # Per pass.v0 contract. No credentials are returned; only routing fields
6
+ # (workspace_id, org_id, seat_template, requested_runtime, artifact_type,
7
+ # allowed_repo_binding).
8
+ #
9
+ # Usage:
10
+ # sh bin/seat-consume-pass --pass-id <pass-id>
11
+ # sh bin/seat-consume-pass --pass-id <pass-id> --host http://localhost:4000
12
+ set -eu
13
+
14
+ ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
15
+ # shellcheck source=_lib/sanitize.sh
16
+ . "$ROOT/bin/_lib/sanitize.sh"
17
+ # shellcheck source=_lib/state.sh
18
+ . "$ROOT/bin/_lib/state.sh"
19
+ # shellcheck source=_lib/http.sh
20
+ . "$ROOT/bin/_lib/http.sh"
21
+
22
+ host=${ETIQUETTE_HOSTED_HOST:-http://localhost:4000}
23
+ pass_id=""
24
+
25
+ while [ "$#" -gt 0 ]; do
26
+ case "$1" in
27
+ --pass-id) pass_id=$2; shift 2 ;;
28
+ --host) host=$2; shift 2 ;;
29
+ -h|--help)
30
+ cat <<'EOF'
31
+ seat-consume-pass — redeem a setup-pass; cache envelope to workspace.json
32
+
33
+ sh bin/seat-consume-pass --pass-id <pass-id> [--host <url>]
34
+
35
+ Requires seat-init to have run first (needs seat.json for consumer_seat_id).
36
+ Stores envelope at $STATE_DIR/workspace.json on success.
37
+ EOF
38
+ exit 0
39
+ ;;
40
+ *) echo "unknown arg: $1" >&2; exit 2 ;;
41
+ esac
42
+ done
43
+
44
+ if [ -z "$pass_id" ]; then
45
+ echo "seat-consume-pass: --pass-id is required" >&2
46
+ exit 2
47
+ fi
48
+
49
+ # Pass-id format check: it's a ULID we received from the dashboard; let the
50
+ # server be the authority on validity, but reject obvious junk (whitespace,
51
+ # excessive length).
52
+ case "$pass_id" in
53
+ *' '*|*"$(printf '\t')"*) echo "seat-consume-pass: pass-id has whitespace" >&2; exit 2 ;;
54
+ esac
55
+ if [ "${#pass_id}" -gt 64 ]; then
56
+ echo "seat-consume-pass: pass-id length > 64; suspicious" >&2
57
+ exit 2
58
+ fi
59
+
60
+ seat_file=$(etq_state_seat_path)
61
+ if [ ! -f "$seat_file" ]; then
62
+ cat >&2 <<EOF
63
+ seat-consume-pass: seat.json missing — run \`sh bin/seat-init\` first.
64
+ EOF
65
+ exit 1
66
+ fi
67
+
68
+ seat_id=$(etq_state_read_key "$seat_file" seat_id) || exit 1
69
+ # Defence: confirm seat_id still sanitiser-clean (in case of manual edits).
70
+ if ! seat_id=$(etq_sanitize_opaque_id "$seat_id"); then
71
+ echo "seat-consume-pass: seat.json contains invalid seat_id; re-run seat-init --force" >&2
72
+ exit 1
73
+ fi
74
+
75
+ # Build request body.
76
+ body=$(printf '{"consumer_seat_id":"%s"}' "$seat_id")
77
+
78
+ url="$host/passes/$pass_id/consume"
79
+ printf 'seat-consume-pass: POST %s\n' "$url"
80
+
81
+ response=$(etq_http_post "$url" "$body") || {
82
+ rc=$?
83
+ echo "seat-consume-pass: pass redemption failed (http rc=$rc)" >&2
84
+ exit "$rc"
85
+ }
86
+
87
+ # Sanity-check the envelope.
88
+ if ! command -v jq >/dev/null 2>&1; then
89
+ echo "seat-consume-pass: jq required to parse envelope" >&2
90
+ exit 1
91
+ fi
92
+
93
+ workspace_id=$(printf '%s' "$response" | jq -r '.workspace_id // empty')
94
+ org_id=$(printf '%s' "$response" | jq -r '.org_id // empty')
95
+
96
+ if [ -z "$workspace_id" ] || [ -z "$org_id" ]; then
97
+ echo "seat-consume-pass: envelope missing workspace_id or org_id" >&2
98
+ printf '%s\n' "$response" >&2
99
+ exit 1
100
+ fi
101
+
102
+ now=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
103
+ # Augment envelope with consumption-side metadata (pass_id + host + ts).
104
+ augmented=$(printf '%s' "$response" | jq \
105
+ --arg pass_id "$pass_id" \
106
+ --arg host "$host" \
107
+ --arg consumed_at "$now" \
108
+ '. + {pass_id: $pass_id, hosted_host: $host, cached_at: $consumed_at}')
109
+
110
+ workspace_file=$(etq_state_workspace_path)
111
+ etq_state_write_file "$workspace_file" "$augmented"
112
+
113
+ printf 'seat-consume-pass: cached envelope to %s\n' "$workspace_file"
114
+ printf 'seat-consume-pass: workspace_id=%s org_id=%s\n' "$workspace_id" "$org_id"
115
+ printf 'seat-consume-pass: next: sh bin/seat-post-readiness\n'
@@ -0,0 +1,20 @@
1
+ #!/bin/sh
2
+ set -eu
3
+
4
+ ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
5
+
6
+ test -f "$ROOT/.llm-substrate/HANDOFF.md"
7
+ test -f "$ROOT/docs/work/ledger/LEDGER.md"
8
+ test -f "$ROOT/docs/work/access/workspace-secure-profile.v0.json"
9
+ test -f "$ROOT/docs/work/access/ACCESS-ASSURANCE.md"
10
+ test -f "$ROOT/docs/work/playbacks/PLAYBACK_TEMPLATE.md"
11
+ test -f "$ROOT/docs/work/tasks/CONTROL-001.yaml"
12
+ test -d "$ROOT/docs/work/receipts"
13
+ test -f "$ROOT/bin/access-assurance-check"
14
+ test -f "$ROOT/bin/session-init"
15
+ test -f "$ROOT/bin/local-event"
16
+ test -f "$ROOT/bin/compact-local-events"
17
+
18
+ sh "$ROOT/bin/access-assurance-check" >/dev/null
19
+
20
+ echo "control-seat: ok"
@@ -0,0 +1,86 @@
1
+ #!/bin/sh
2
+ # seat-init: mint an opaque seat_id and write $STATE_DIR/seat.json.
3
+ #
4
+ # Opaque seat_id only: no hostnames, paths, IPs, unsafe characters, or
5
+ # values over 64 chars. Idempotent: re-runs preserve existing seat_id
6
+ # unless --force is passed.
7
+ #
8
+ # Usage:
9
+ # sh bin/seat-init # mint if missing
10
+ # sh bin/seat-init --execution-seat cc-seat # set seat label
11
+ # sh bin/seat-init --force # re-mint even if seat.json exists
12
+ set -eu
13
+
14
+ ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
15
+ # shellcheck source=_lib/sanitize.sh
16
+ . "$ROOT/bin/_lib/sanitize.sh"
17
+ # shellcheck source=_lib/state.sh
18
+ . "$ROOT/bin/_lib/state.sh"
19
+
20
+ execution_seat=${ETIQUETTE_EXECUTION_SEAT:-control-seat}
21
+ force=0
22
+ while [ "$#" -gt 0 ]; do
23
+ case "$1" in
24
+ --execution-seat) execution_seat=$2; shift 2 ;;
25
+ --force) force=1; shift ;;
26
+ -h|--help)
27
+ cat <<'EOF'
28
+ seat-init — mint opaque seat_id; bootstrap $STATE_DIR/seat.json
29
+
30
+ sh bin/seat-init [--execution-seat <label>] [--force]
31
+
32
+ Writes ~/.etiquette/state/seat.json under the seat root with:
33
+ seat_id opaque ULID-style id (≤64 chars; no hostnames/paths/IPs)
34
+ execution_seat caller-supplied label (default control-seat)
35
+ created_at iso8601 timestamp
36
+
37
+ Idempotent: re-runs preserve seat_id unless --force.
38
+ EOF
39
+ exit 0
40
+ ;;
41
+ *) echo "unknown arg: $1" >&2; exit 2 ;;
42
+ esac
43
+ done
44
+
45
+ # Validate execution_seat is sanitiser-clean (it ends up in payloads).
46
+ if ! execution_seat=$(etq_sanitize_opaque_id "$execution_seat"); then
47
+ exit 1
48
+ fi
49
+
50
+ etq_state_init_dir
51
+ seat_file=$(etq_state_seat_path)
52
+
53
+ if [ -f "$seat_file" ] && [ "$force" -eq 0 ]; then
54
+ existing_id=$(etq_state_read_key "$seat_file" seat_id 2>/dev/null || true)
55
+ if [ -n "$existing_id" ]; then
56
+ printf 'seat-init: seat.json already exists (seat_id=%s); use --force to re-mint\n' "$existing_id"
57
+ exit 0
58
+ fi
59
+ fi
60
+
61
+ # Generate an opaque seat_id. Shape: seat-<unixtime>-<8 hex>
62
+ # This is opaque (no hostname/path/IP), bounded length, sanitiser-clean.
63
+ ts=$(date +%s)
64
+ if command -v openssl >/dev/null 2>&1; then
65
+ rand=$(openssl rand -hex 4)
66
+ elif [ -r /dev/urandom ]; then
67
+ rand=$(od -An -N4 -tx1 /dev/urandom | tr -d ' \n')
68
+ else
69
+ # Last-resort PRNG; not cryptographic, but seat_id is not a secret.
70
+ rand=$(awk 'BEGIN{srand(); printf "%08x", int(rand()*4294967295)}')
71
+ fi
72
+ seat_id="seat-${ts}-${rand}"
73
+
74
+ # Verify our own output is sanitiser-clean (defence-in-depth).
75
+ if ! seat_id=$(etq_sanitize_opaque_id "$seat_id"); then
76
+ echo "seat-init: generated seat_id failed self-sanitiser; aborting" >&2
77
+ exit 1
78
+ fi
79
+
80
+ now=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
81
+ body=$(printf '{"seat_id":"%s","execution_seat":"%s","created_at":"%s"}' \
82
+ "$seat_id" "$execution_seat" "$now")
83
+
84
+ etq_state_write_file "$seat_file" "$body"
85
+ printf 'seat-init: minted seat_id=%s execution_seat=%s\n' "$seat_id" "$execution_seat"
86
+ printf 'seat-init: wrote %s\n' "$seat_file"
@@ -0,0 +1,171 @@
1
+ #!/bin/sh
2
+ # seat-post-readiness: push capability declaration to the hosted plane.
3
+ #
4
+ # Per hosted-plane-boundary.v0 Sycophant problem fix — capability_source
5
+ # defaults to "seat-doctor" (trusted source). "agent-self-declared" is
6
+ # allowed but flagged low-trust at the dashboard. Operator-config is
7
+ # also trusted.
8
+ #
9
+ # Usage:
10
+ # sh bin/seat-post-readiness --status operational
11
+ # sh bin/seat-post-readiness --status operational --parallel-support \
12
+ # --strategies "solo,telescope,local_adversarial_tango" \
13
+ # --unsupported "avengers"
14
+ # sh bin/seat-post-readiness --capability-source operator-config --status operational
15
+ set -eu
16
+
17
+ ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
18
+ # shellcheck source=_lib/sanitize.sh
19
+ . "$ROOT/bin/_lib/sanitize.sh"
20
+ # shellcheck source=_lib/state.sh
21
+ . "$ROOT/bin/_lib/state.sh"
22
+ # shellcheck source=_lib/http.sh
23
+ . "$ROOT/bin/_lib/http.sh"
24
+
25
+ ALLOWED_STATUSES="operational degraded blocked"
26
+ ALLOWED_SOURCES="operator-config seat-doctor agent-self-declared"
27
+
28
+ status=""
29
+ capability_source="seat-doctor"
30
+ parallel_support="false"
31
+ strategies=""
32
+ unsupported=""
33
+ environment_node_id=""
34
+
35
+ while [ "$#" -gt 0 ]; do
36
+ case "$1" in
37
+ --status) status=$2; shift 2 ;;
38
+ --capability-source) capability_source=$2; shift 2 ;;
39
+ --parallel-support) parallel_support="true"; shift ;;
40
+ --strategies) strategies=$2; shift 2 ;;
41
+ --unsupported) unsupported=$2; shift 2 ;;
42
+ --environment-node-id) environment_node_id=$2; shift 2 ;;
43
+ -h|--help)
44
+ cat <<EOF
45
+ seat-post-readiness — push capability declaration to the hosted plane
46
+
47
+ sh bin/seat-post-readiness --status <status> \\
48
+ [--capability-source <source>] [--parallel-support] \\
49
+ [--strategies "a,b,c"] [--unsupported "x,y"] \\
50
+ [--environment-node-id <opaque>]
51
+
52
+ Status (3):
53
+ $(printf ' - %s\n' $ALLOWED_STATUSES)
54
+
55
+ Capability source (3; agent-self-declared flagged low-trust):
56
+ $(printf ' - %s\n' $ALLOWED_SOURCES)
57
+
58
+ Defaults: capability_source=seat-doctor, parallel_support=false.
59
+ Requires seat-consume-pass to have populated workspace.json.
60
+ EOF
61
+ exit 0
62
+ ;;
63
+ *) echo "unknown arg: $1" >&2; exit 2 ;;
64
+ esac
65
+ done
66
+
67
+ [ -n "$status" ] || { echo "seat-post-readiness: --status required" >&2; exit 2; }
68
+
69
+ # Allowlist checks.
70
+ ok=0
71
+ for s in $ALLOWED_STATUSES; do [ "$s" = "$status" ] && ok=1; done
72
+ if [ "$ok" -ne 1 ]; then
73
+ printf 'seat-post-readiness: --status "%s" not in allowlist:\n' "$status" >&2
74
+ printf ' - %s\n' $ALLOWED_STATUSES >&2
75
+ exit 2
76
+ fi
77
+ ok=0
78
+ for s in $ALLOWED_SOURCES; do [ "$s" = "$capability_source" ] && ok=1; done
79
+ if [ "$ok" -ne 1 ]; then
80
+ printf 'seat-post-readiness: --capability-source "%s" not in allowlist:\n' "$capability_source" >&2
81
+ printf ' - %s\n' $ALLOWED_SOURCES >&2
82
+ exit 2
83
+ fi
84
+
85
+ # Sanitise environment_node_id if supplied.
86
+ clean_env=""
87
+ if [ -n "$environment_node_id" ]; then
88
+ if ! clean_env=$(etq_sanitize_opaque_id "$environment_node_id"); then
89
+ echo "seat-post-readiness: --environment-node-id rejected" >&2
90
+ exit 2
91
+ fi
92
+ fi
93
+
94
+ # Load state.
95
+ seat_file=$(etq_state_seat_path)
96
+ workspace_file=$(etq_state_workspace_path)
97
+ if [ ! -f "$seat_file" ]; then
98
+ echo "seat-post-readiness: seat.json missing — run \`sh bin/seat-init\` first" >&2
99
+ exit 1
100
+ fi
101
+ if [ ! -f "$workspace_file" ]; then
102
+ echo "seat-post-readiness: workspace.json missing — run \`sh bin/seat-consume-pass --pass-id <id>\` first" >&2
103
+ exit 1
104
+ fi
105
+
106
+ seat_id=$(etq_state_read_key "$seat_file" seat_id) || exit 1
107
+ execution_seat=$(etq_state_read_key "$seat_file" execution_seat) || exit 1
108
+ workspace_id=$(etq_state_read_key "$workspace_file" workspace_id) || exit 1
109
+ pass_id=$(etq_state_read_key "$workspace_file" pass_id) || exit 1
110
+ host=$(etq_state_read_key "$workspace_file" hosted_host) || exit 1
111
+
112
+ # Validate seat_id + execution_seat against sanitiser (defence).
113
+ seat_id=$(etq_sanitize_opaque_id "$seat_id") || exit 1
114
+ execution_seat=$(etq_sanitize_opaque_id "$execution_seat") || exit 1
115
+
116
+ # Convert comma-separated strategy lists to JSON arrays.
117
+ strategies_to_json() {
118
+ list=$1
119
+ if [ -z "$list" ]; then
120
+ printf '[]'
121
+ return
122
+ fi
123
+ # Use jq for safe construction.
124
+ printf '%s' "$list" | tr ',' '\n' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | grep -v '^$' \
125
+ | jq -R . | jq -sc .
126
+ }
127
+ if ! command -v jq >/dev/null 2>&1; then
128
+ echo "seat-post-readiness: jq required" >&2
129
+ exit 1
130
+ fi
131
+ strategies_json=$(strategies_to_json "$strategies")
132
+ unsupported_json=$(strategies_to_json "$unsupported")
133
+
134
+ # Build body.
135
+ body=$(jq -nc \
136
+ --arg pass_id "$pass_id" \
137
+ --arg seat_id "$seat_id" \
138
+ --arg execution_seat "$execution_seat" \
139
+ --arg status "$status" \
140
+ --arg capability_source "$capability_source" \
141
+ --argjson parallel_support "$parallel_support" \
142
+ --argjson strategies "$strategies_json" \
143
+ --argjson unsupported "$unsupported_json" \
144
+ --arg environment_node_id "$clean_env" \
145
+ '
146
+ {pass_id: $pass_id, seat_id: $seat_id, execution_seat: $execution_seat,
147
+ status: $status, capability_source: $capability_source,
148
+ parallel_support: $parallel_support,
149
+ supported_strategies: $strategies,
150
+ unsupported_strategies: $unsupported}
151
+ + (if $environment_node_id != "" then {environment_node_id: $environment_node_id} else {} end)
152
+ ')
153
+
154
+ url="$host/workspaces/$workspace_id/seat-readiness"
155
+ printf 'seat-post-readiness: POST %s status=%s capability_source=%s\n' \
156
+ "$url" "$status" "$capability_source"
157
+
158
+ response=$(etq_http_post "$url" "$body") || {
159
+ rc=$?
160
+ echo "seat-post-readiness: ingest failed (http rc=$rc)" >&2
161
+ exit "$rc"
162
+ }
163
+
164
+ printf 'seat-post-readiness: ingested\n'
165
+ printf '%s\n' "$response" | jq -c . 2>/dev/null || printf '%s\n' "$response"
166
+
167
+ # Capability-source hint if low-trust.
168
+ if [ "$capability_source" = "agent-self-declared" ]; then
169
+ printf '\nseat-post-readiness: NOTE — capability_source=agent-self-declared is flagged low-trust\n' >&2
170
+ printf 'seat-post-readiness: in the dashboard per capability-trust doctrine.\n' >&2
171
+ fi
@@ -0,0 +1,132 @@
1
+ #!/bin/sh
2
+ # session-init: initialize the live local workflow surface for this seat.
3
+ #
4
+ # This is intentionally local-first. It does not register the seat remotely,
5
+ # consume a pass, start a runtime, clone product repos, or call hosted APIs.
6
+ # It verifies the seat identity, prepares ledger/receipt/task directories,
7
+ # writes session.json, appends one compact ledger line, and emits one local
8
+ # event so a running agent session has a durable local trail from turn zero.
9
+ set -eu
10
+
11
+ ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
12
+ # shellcheck source=_lib/sanitize.sh
13
+ . "$ROOT/bin/_lib/sanitize.sh"
14
+ # shellcheck source=_lib/state.sh
15
+ . "$ROOT/bin/_lib/state.sh"
16
+
17
+ task_id="session-bootstrap"
18
+ operator_principal="local-operator"
19
+ force=0
20
+
21
+ while [ "$#" -gt 0 ]; do
22
+ case "$1" in
23
+ --task-id) task_id=$2; shift 2 ;;
24
+ --operator) operator_principal=$2; shift 2 ;;
25
+ --force) force=1; shift ;;
26
+ -h|--help)
27
+ cat <<'EOF'
28
+ session-init — prepare local workflow surface for a live seat session
29
+
30
+ sh bin/session-init [--task-id <id>] [--operator <id>] [--force]
31
+
32
+ Requires seat-init first. If seat-consume-pass already ran, the hosted
33
+ workspace_id is copied into session.json. This command is local-only: no
34
+ hosted POST, no product repo clone, no execution, no grant attachment.
35
+ EOF
36
+ exit 0
37
+ ;;
38
+ *) echo "unknown arg: $1" >&2; exit 2 ;;
39
+ esac
40
+ done
41
+
42
+ seat_file=$(etq_state_seat_path)
43
+ if [ ! -f "$seat_file" ]; then
44
+ echo "session-init: seat.json missing — run \`sh bin/seat-init\` first" >&2
45
+ exit 1
46
+ fi
47
+
48
+ seat_id=$(etq_state_read_key "$seat_file" seat_id) || exit 1
49
+ execution_seat=$(etq_state_read_key "$seat_file" execution_seat) || exit 1
50
+ seat_id=$(etq_sanitize_opaque_id "$seat_id") || exit 1
51
+ execution_seat=$(etq_sanitize_opaque_id "$execution_seat") || exit 1
52
+ operator_principal=$(etq_sanitize_opaque_id "$operator_principal") || exit 1
53
+ task_id=$(etq_sanitize_opaque_id "$task_id") || exit 1
54
+
55
+ etq_state_init_dir
56
+ session_file="$(etq_state_dir)/session.json"
57
+ if [ -f "$session_file" ] && [ "$force" -eq 0 ]; then
58
+ existing=$(etq_state_read_key "$session_file" session_id 2>/dev/null || true)
59
+ if [ -n "$existing" ]; then
60
+ printf 'session-init: session already exists (session_id=%s); use --force to re-open\n' "$existing"
61
+ exit 0
62
+ fi
63
+ fi
64
+
65
+ mkdir -p \
66
+ "$ROOT/.etiquette/events" \
67
+ "$ROOT/docs/work/ledger" \
68
+ "$ROOT/docs/work/receipts" \
69
+ "$ROOT/docs/work/tasks" \
70
+ "$ROOT/docs/work/outbox" \
71
+ "$ROOT/docs/work/sessions"
72
+
73
+ ledger="$ROOT/docs/work/ledger/LEDGER.md"
74
+ if [ ! -f "$ledger" ]; then
75
+ printf '# Local Ledger\n\n' > "$ledger"
76
+ fi
77
+
78
+ ts=$(date +%s)
79
+ if command -v openssl >/dev/null 2>&1; then
80
+ rand=$(openssl rand -hex 4)
81
+ elif [ -r /dev/urandom ]; then
82
+ rand=$(od -An -N4 -tx1 /dev/urandom | tr -d ' \n')
83
+ else
84
+ rand=$(awk 'BEGIN{srand(); printf "%08x", int(rand()*4294967295)}')
85
+ fi
86
+ session_id="session-${ts}-${rand}"
87
+ session_id=$(etq_sanitize_opaque_id "$session_id") || exit 1
88
+ now=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
89
+
90
+ workspace_id=""
91
+ hosted_host=""
92
+ workspace_file=$(etq_state_workspace_path)
93
+ if [ -f "$workspace_file" ]; then
94
+ workspace_id=$(etq_state_read_key "$workspace_file" workspace_id 2>/dev/null || true)
95
+ hosted_host=$(etq_state_read_key "$workspace_file" hosted_host 2>/dev/null || true)
96
+ fi
97
+
98
+ body=$(jq -cn \
99
+ --arg session_id "$session_id" \
100
+ --arg started_at "$now" \
101
+ --arg seat_id "$seat_id" \
102
+ --arg execution_seat "$execution_seat" \
103
+ --arg operator_principal "$operator_principal" \
104
+ --arg task_id "$task_id" \
105
+ --arg remote_registration "$([ -n "$workspace_id" ] && printf present || printf absent)" \
106
+ --arg workspace_id "$workspace_id" \
107
+ --arg hosted_host "$hosted_host" \
108
+ '{
109
+ session_id: $session_id,
110
+ started_at: $started_at,
111
+ seat_id: $seat_id,
112
+ execution_seat: $execution_seat,
113
+ operator_principal: $operator_principal,
114
+ task_id: $task_id,
115
+ workflow_surface: "local-control-seat",
116
+ remote_registration: $remote_registration
117
+ }
118
+ + (if $workspace_id != "" then {workspace_id: $workspace_id} else {} end)
119
+ + (if $hosted_host != "" then {hosted_host: $hosted_host} else {} end)')
120
+
121
+ etq_state_write_file "$session_file" "$body"
122
+ printf -- '- %s session=%s task=%s seat=%s operator=%s action=session_opened scope=local-workflow remote_registration=%s authority=advisory-only\n' \
123
+ "$now" "$session_id" "$task_id" "$seat_id" "$operator_principal" \
124
+ "$([ -n "$workspace_id" ] && printf present || printf absent)" >> "$ledger"
125
+
126
+ sh "$ROOT/bin/local-event" "$task_id" "$seat_id" session_init \
127
+ "session $session_id opened for $execution_seat; local ledger/receipts/tasks/outbox ready" >/dev/null
128
+
129
+ printf 'session-init: opened session_id=%s task_id=%s seat_id=%s\n' "$session_id" "$task_id" "$seat_id"
130
+ printf 'session-init: wrote %s\n' "$session_file"
131
+ printf 'session-init: ledger ready at docs/work/ledger/LEDGER.md\n'
132
+ printf 'session-init: receipts/tasks/outbox ready under docs/work/\n'
@@ -0,0 +1,39 @@
1
+ # Access Assurance
2
+
3
+ The vanilla control seat starts in `open` mode. That keeps Etiquette usable as a
4
+ local-first workflow kit without authentication, screening, hosted services, or
5
+ a daemon.
6
+
7
+ The workspace mode lives in `workspace-secure-profile.v0.json`.
8
+
9
+ Modes:
10
+
11
+ - `open`: no access-assurance gate is required.
12
+ - `screening-warn`: restricted-seat policy and claims are checked, but failures
13
+ warn instead of blocking.
14
+ - `screening-enforced`: restricted seats fail closed when policy or claims are
15
+ missing, expired, revoked, or not provider-backed.
16
+ - `enterprise-reactor`: `screening-enforced` plus an optional tighten-only
17
+ daemon for revocation sync, badge refresh, kill-switch writes, and outbox
18
+ drain.
19
+
20
+ The reactor may tighten access only. It must not grant access, authorize
21
+ actions, close work, merge, or rewrite the ledger.
22
+
23
+ Provider-backed secure deployments should keep raw identity material outside the
24
+ repo:
25
+
26
+ ```text
27
+ Persona / Scandit / SSO -> Supabase derived claims -> local access check
28
+ ```
29
+
30
+ Repo-safe fields are provider refs, status, policy eligibility class, evidence
31
+ hash, expiry, and audit timestamps. Passport images, document photos, full
32
+ document numbers, ethnicity, provider secrets, and raw webhook payloads do not
33
+ belong in Git.
34
+
35
+ Check the mode:
36
+
37
+ ```sh
38
+ sh bin/access-assurance-check
39
+ ```
@@ -0,0 +1,53 @@
1
+ {
2
+ "schema": "workspace-secure-profile.v0",
3
+ "workspace_id": "vanilla-control-seat",
4
+ "mode": "open",
5
+ "allowed_modes": [
6
+ "open",
7
+ "screening-warn",
8
+ "screening-enforced",
9
+ "enterprise-reactor"
10
+ ],
11
+ "mode_meanings": {
12
+ "open": "No access-assurance gate is required. Etiquette core remains local and unauthenticated.",
13
+ "screening-warn": "Restricted-seat claims are checked and reported, but missing or failed claims do not block local entry.",
14
+ "screening-enforced": "Restricted-seat claims are required before routing or entering restricted seats.",
15
+ "enterprise-reactor": "screening-enforced plus an optional tighten-only reactor for revocation sync, badge refresh, kill-switch writes, and outbox drain."
16
+ },
17
+ "identity_model": {
18
+ "authenticate": "operator_principals",
19
+ "assign_to": "seat_ids",
20
+ "authorize": "per_action_grants",
21
+ "workspace_role": "policy_scope",
22
+ "linear_role": "external_backlog_projection_only"
23
+ },
24
+ "claim_source": {
25
+ "kind": "none-required-for-open-mode",
26
+ "live_replacement": "Supabase Auth plus derived Persona and Scandit claim export",
27
+ "raw_pii_allowed_in_repo": false
28
+ },
29
+ "reactor": {
30
+ "required_for_mode": "enterprise-reactor",
31
+ "default_enabled": false,
32
+ "tighten_only": true,
33
+ "may_write": [
34
+ "kill_switch_entries",
35
+ "expired_assurance_markers",
36
+ "badge_refresh_cache",
37
+ "sync_outbox"
38
+ ],
39
+ "must_not": [
40
+ "grant_access",
41
+ "close_work",
42
+ "merge",
43
+ "rewrite_ledger",
44
+ "authorize_actions"
45
+ ]
46
+ },
47
+ "authority_boundary": {
48
+ "profile_can_authorize_work": false,
49
+ "profile_can_grant_seat": false,
50
+ "profile_can_merge_or_close": false,
51
+ "reactor_can_grant_or_loosen_access": false
52
+ }
53
+ }
@@ -0,0 +1,7 @@
1
+ # Control Seat Ledger
2
+
3
+ | id | status | owner | title | validation |
4
+ | --- | --- | --- | --- | --- |
5
+ | CONTROL-001 | open | control-seat | Prove vanilla Etiquette startup | `sh bin/seat-doctor` |
6
+
7
+ Receipts live under `docs/work/receipts/`.
@@ -0,0 +1,31 @@
1
+ # Manual Playback Template
2
+
3
+ ```yaml
4
+ playback_id:
5
+ timeframe:
6
+ scope:
7
+ operator_principal:
8
+ execution_seat:
9
+ inputs:
10
+ receipts: []
11
+ ledger_refs: []
12
+ commit_refs: []
13
+ decisions:
14
+ - decision:
15
+ evidence_refs: []
16
+ lessons:
17
+ - lesson:
18
+ evidence_refs: []
19
+ deferred:
20
+ - item:
21
+ reason:
22
+ proposed_actions:
23
+ - action:
24
+ type: no_change | docs_patch | memory_save | controlled_drill
25
+ evidence_refs: []
26
+ operator_decision_required:
27
+ controlled_drill: true
28
+ batch_approval_allowed: false
29
+ ```
30
+
31
+ Use this after meaningful workflow change. Keep it evidence-bound: cite receipts, ledger rows, commits, and validation output. Do not treat chat as proof.
@@ -0,0 +1,13 @@
1
+ id: CONTROL-001
2
+ status: open
3
+ owner: control-seat
4
+ mode: docs-only
5
+ title: Prove vanilla Etiquette startup
6
+ purpose:
7
+ - validate the control seat without provisioning runtime-specific seats
8
+ validation:
9
+ - sh bin/seat-doctor
10
+ expected_return:
11
+ - readiness receipt
12
+ - decision whether more seats are needed
13
+
@@ -0,0 +1,6 @@
1
+ # Etiquette Kernel
2
+
3
+ Shared contracts and validation vocabulary for Etiquette.
4
+
5
+ The kernel is runtime-neutral. It does not assume Claude Code, Codex, Gemini, Slack, GitHub, or a hosted service.
6
+