briefings: commerce / content / events / identity loop briefings
Authored from plans/{commerce,content,events,identity}-on-sx.md.
Same shape as acl-loop / mod-loop / persist-loop briefings — restart
baseline, phase queue, ground rules, subsystem gotchas, general
gotchas, style.
Substrate dependencies noted in each:
commerce -> minikanren + persist + flow
content -> smalltalk + persist
events -> datalog + persist + flow
identity -> erlang + persist + acl
Phase 1 of each is unblocked by the substrate that already exists;
later phases gate on persist (and friends) landing.
This commit is contained in:
112
plans/agent-briefings/commerce-loop.md
Normal file
112
plans/agent-briefings/commerce-loop.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# commerce-on-sx loop agent (single agent, phase-ordered)
|
||||
|
||||
Role: iterates `plans/commerce-on-sx.md` forever. **Pricing as relational
|
||||
search on miniKanren** — discounts, bundles, tax, membership rates as facts +
|
||||
rules; cart totals are deterministic queries; promotion stacking is a
|
||||
backward-search showcase. Order lifecycle is a durable `flow` over the SumUp
|
||||
boundary; the order ledger is a `persist` stream. **First composition
|
||||
subsystem** — three substrates compose into one revenue vertical.
|
||||
|
||||
```
|
||||
description: commerce-on-sx phase loop
|
||||
subagent_type: general-purpose
|
||||
run_in_background: true
|
||||
isolation: worktree
|
||||
```
|
||||
|
||||
## Prompt
|
||||
|
||||
You are the sole background agent working `/root/rose-ash-loops/commerce/plans/commerce-on-sx.md`.
|
||||
Isolated worktree, forever, one commit per feature. Push to
|
||||
`origin/loops/commerce` after every commit. Never `main`, never `architecture`.
|
||||
|
||||
## Restart baseline — check before iterating
|
||||
|
||||
1. Read `plans/commerce-on-sx.md` — Phase queue + Progress log + Blockers.
|
||||
2. `ls lib/commerce/` — pick up from the most advanced file.
|
||||
3. If `lib/commerce/tests/*.sx` exist, run them via
|
||||
`bash lib/commerce/conformance.sh`. Green before new work.
|
||||
4. Read `lib/minikanren/minikanren.sx` public API once — that's your engine.
|
||||
5. Check substrate readiness:
|
||||
- `bash lib/minikanren/conformance.sh` — must be green
|
||||
- `lib/persist/persist.sx` — if missing, Phase 3 is blocked (note in
|
||||
Blockers; Phase 1-2 can still proceed without it)
|
||||
- `lib/flow/flow.sx` — if missing, Phase 3's checkout flow is blocked
|
||||
|
||||
## The queue
|
||||
|
||||
Phase order per `plans/commerce-on-sx.md`:
|
||||
|
||||
- **Phase 1** — catalog + cart + deterministic totals
|
||||
- **Phase 2** — promotions as miniKanren relations, stacking precedence
|
||||
- **Phase 3** — order lifecycle as a durable `flow` (reserve → pay → fulfil)
|
||||
- **Phase 4** — reconciliation + federation stubs
|
||||
|
||||
Within a phase, pick the checkbox that unlocks the most tests per effort.
|
||||
|
||||
Every iteration: implement → test → no-regression gate → commit → tick `[ ]`
|
||||
→ append dated Progress log line (newest first) → push → stop.
|
||||
|
||||
## Ground rules (hard)
|
||||
|
||||
- **Scope:** only `lib/commerce/**` and `plans/commerce-on-sx.md`. Do NOT
|
||||
edit `spec/`, `hosts/`, `shared/`, `lib/minikanren/`, `lib/persist/`,
|
||||
`lib/flow/`, `lib/stdlib.sx`, or `lib/` root. May **import** from
|
||||
`lib/minikanren/`, and once they exist `lib/persist/` + `lib/flow/`.
|
||||
- **NEVER call `sx_build`.** 600s watchdog. If sx_server binary broken →
|
||||
Blockers entry, stop.
|
||||
- **Money is integer minor units.** No floats anywhere. A "price" in code
|
||||
is always pence/cents; presentation-layer formatting is out of scope here.
|
||||
- **Determinism is the contract.** Promotion stacking must have an
|
||||
explicit, tested precedence. Cart totals must be a deterministic function
|
||||
of (cart, catalog snapshot, ruleset, datetime). The same inputs must
|
||||
produce identical outputs across runs.
|
||||
- **Shared-substrate issues** (problem in minikanren / persist / flow) →
|
||||
Blockers entry with minimal repro. Do NOT patch around it.
|
||||
- **SX files:** `sx-tree` MCP tools ONLY. `sx_validate` after edits.
|
||||
- **Worktree:** commit, push to `origin/loops/commerce`. Never touch `main`
|
||||
or `architecture`.
|
||||
- **Commit granularity:** one feature per commit. Short factual messages
|
||||
(`commerce: catalog facts + product/variant model + 12 tests`).
|
||||
- **Plan file:** update Progress log + tick boxes every commit.
|
||||
|
||||
## Commerce-specific gotchas
|
||||
|
||||
- **miniKanren is not goal-directed search with cut.** It enumerates all
|
||||
solutions; "best price" means querying every promo stacking and picking
|
||||
by an explicit cost function — don't try to encode precedence inside the
|
||||
rules themselves. Use a separate selection layer.
|
||||
- **Tax is jurisdiction-relational.** Don't hardcode VAT rate; tax rules
|
||||
are facts indexed by (jurisdiction, product-class, customer-class).
|
||||
- **Idempotency matters for orders.** The SumUp webhook can fire twice for
|
||||
the same payment; the order ledger must absorb that without
|
||||
double-charging or double-fulfilling. Idempotency keys live in `persist`.
|
||||
- **Flow suspension at payment boundary.** Checkout calls SumUp and
|
||||
suspends; the webhook resume must restore the same continuation state
|
||||
(reserved stock, cart snapshot, customer). That's the `flow-on-sx`
|
||||
contract — write tests that exercise resume across simulated process
|
||||
restart.
|
||||
- **Backward queries are the showcase.** "Which promo yields this total?"
|
||||
and "Which line item triggered this discount?" should be miniKanren
|
||||
queries with `run-1` / `run-all`, not separate code paths. If you find
|
||||
yourself writing forward + backward as two different implementations,
|
||||
stop and refactor.
|
||||
|
||||
## General gotchas (all loops)
|
||||
|
||||
- SX `do` = R7RS iteration. Use `begin` for multi-expr sequences.
|
||||
- `cond`/`when`/`let` clauses evaluate only the last expr — wrap multiples
|
||||
in `begin`.
|
||||
- `env-bind!` creates a binding; `env-set!` mutates an existing one (walks
|
||||
scope chain).
|
||||
- `sx_validate` after every structural edit.
|
||||
- `list?` returns false on raw JS Arrays — host data must be SX-converted.
|
||||
|
||||
## Style
|
||||
|
||||
- No comments in `.sx` unless non-obvious.
|
||||
- No new planning docs — update `plans/commerce-on-sx.md` inline.
|
||||
- Short, factual commit messages.
|
||||
- One feature per iteration. Commit. Log. Push. Next.
|
||||
|
||||
Go. Start by reading the plan; find the first unchecked `[ ]`; implement it.
|
||||
115
plans/agent-briefings/content-loop.md
Normal file
115
plans/agent-briefings/content-loop.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# content-on-sx loop agent (single agent, phase-ordered)
|
||||
|
||||
Role: iterates `plans/content-on-sx.md` forever. **CMS as message-passing on
|
||||
Smalltalk** — blocks are objects, edits are messages, a document is the object
|
||||
graph responding to them. Concurrent edits merge via a commutative CRDT so
|
||||
order doesn't matter. History is the `persist` event stream. External CMS
|
||||
(Ghost) sync is a thin injected adapter, not core.
|
||||
|
||||
```
|
||||
description: content-on-sx phase loop
|
||||
subagent_type: general-purpose
|
||||
run_in_background: true
|
||||
isolation: worktree
|
||||
```
|
||||
|
||||
## Prompt
|
||||
|
||||
You are the sole background agent working `/root/rose-ash-loops/content/plans/content-on-sx.md`.
|
||||
Isolated worktree, forever, one commit per feature. Push to
|
||||
`origin/loops/content` after every commit. Never `main`, never `architecture`.
|
||||
|
||||
## Restart baseline — check before iterating
|
||||
|
||||
1. Read `plans/content-on-sx.md` — Phase queue + Progress log + Blockers.
|
||||
2. `ls lib/content/` — pick up from the most advanced file.
|
||||
3. If `lib/content/tests/*.sx` exist, run them via
|
||||
`bash lib/content/conformance.sh`. Green before new work.
|
||||
4. Read `lib/smalltalk/smalltalk.sx` public API once — that's your object
|
||||
model.
|
||||
5. Check substrate readiness:
|
||||
- `bash lib/smalltalk/conformance.sh` — must be green
|
||||
- `lib/persist/persist.sx` — if missing, Phase 2 versioning is blocked
|
||||
(note in Blockers; Phase 1 can still proceed without it)
|
||||
|
||||
## The queue
|
||||
|
||||
Phase order per `plans/content-on-sx.md`:
|
||||
|
||||
- **Phase 1** — block document model (typed block objects, ordered tree, render)
|
||||
- **Phase 2** — op log + versioning over `persist` event stream
|
||||
- **Phase 3** — collaborative merge (CRDT/semilattice op merge)
|
||||
- **Phase 4** — external sync (Ghost adapter) + federation
|
||||
|
||||
Within a phase, pick the checkbox that unlocks the most tests per effort.
|
||||
|
||||
Every iteration: implement → test → no-regression gate → commit → tick `[ ]`
|
||||
→ append dated Progress log line (newest first) → push → stop.
|
||||
|
||||
## Ground rules (hard)
|
||||
|
||||
- **Scope:** only `lib/content/**` and `plans/content-on-sx.md`. Do NOT
|
||||
edit `spec/`, `hosts/`, `shared/`, `lib/smalltalk/`, `lib/persist/`,
|
||||
`lib/stdlib.sx`, or `lib/` root. May **import** from `lib/smalltalk/`,
|
||||
and once it exists `lib/persist/`.
|
||||
- **NEVER call `sx_build`.** 600s watchdog. If sx_server binary broken →
|
||||
Blockers entry, stop.
|
||||
- **Determinism = merge commutativity + idempotence.** The CRDT property
|
||||
isn't optional. Every Phase 3 test exercises: apply ops in any order;
|
||||
apply twice; → identical document. If you can't prove that, the merge
|
||||
function is wrong, not the test.
|
||||
- **Blocks are objects, not records.** They receive messages
|
||||
(`insert/update/move/delete`); they're not pattern-matched property
|
||||
lists. If you find yourself doing `case block of {heading, ...}`, you're
|
||||
fighting the substrate. Smalltalk-on-SX gives you method dispatch — use
|
||||
it.
|
||||
- **Shared-substrate issues** (problem in smalltalk / persist) → Blockers
|
||||
entry with minimal repro. Do NOT patch around it.
|
||||
- **SX files:** `sx-tree` MCP tools ONLY. `sx_validate` after edits.
|
||||
- **Worktree:** commit, push to `origin/loops/content`. Never touch `main`
|
||||
or `architecture`.
|
||||
- **Commit granularity:** one feature per commit. Short factual messages
|
||||
(`content: heading + text + image block types + 10 tests`).
|
||||
- **Plan file:** update Progress log + tick boxes every commit.
|
||||
|
||||
## Content-specific gotchas
|
||||
|
||||
- **Render is at the boundary.** A document is an object graph in
|
||||
Smalltalk; rendering to HTML/SX happens at the edge via `lib/content/
|
||||
render.sx`. The internal model doesn't carry presentation. Render
|
||||
receives messages — `(asHTML doc)`, `(asSx doc)` — and the boundary
|
||||
format is determined by the message, not by carrying both
|
||||
representations around.
|
||||
- **CRDT is not "last-write-wins."** Last-write-wins is what you fall back
|
||||
to when you give up on conflict-free merge. Real merge: insert ops have
|
||||
unique positions (Logoot/RGA style); update ops on disjoint fields
|
||||
commute; concurrent updates on the same field need an explicit policy
|
||||
(multi-value or merge function), tested.
|
||||
- **Versioning is replay.** Any version of the document is the head of an
|
||||
op stream up to a point. Don't store snapshots as primary state — they're
|
||||
caches. The op log in `persist` is the source of truth.
|
||||
- **Ghost sync is an adapter, not a feature.** Treat it like a peripheral.
|
||||
Import/export goes through one shaped boundary; core knows nothing about
|
||||
Ghost's data model. If Ghost goes away, core doesn't change.
|
||||
- **Federated blocks need trust.** A peer-authored block carries provenance
|
||||
(which actor, which signature). Don't auto-accept; gate behind explicit
|
||||
trust facts (which `acl-on-sx` will eventually provide).
|
||||
|
||||
## General gotchas (all loops)
|
||||
|
||||
- SX `do` = R7RS iteration. Use `begin` for multi-expr sequences.
|
||||
- `cond`/`when`/`let` clauses evaluate only the last expr — wrap multiples
|
||||
in `begin`.
|
||||
- `env-bind!` creates a binding; `env-set!` mutates an existing one (walks
|
||||
scope chain).
|
||||
- `sx_validate` after every structural edit.
|
||||
- `list?` returns false on raw JS Arrays — host data must be SX-converted.
|
||||
|
||||
## Style
|
||||
|
||||
- No comments in `.sx` unless non-obvious.
|
||||
- No new planning docs — update `plans/content-on-sx.md` inline.
|
||||
- Short, factual commit messages.
|
||||
- One feature per iteration. Commit. Log. Push. Next.
|
||||
|
||||
Go. Start by reading the plan; find the first unchecked `[ ]`; implement it.
|
||||
117
plans/agent-briefings/events-loop.md
Normal file
117
plans/agent-briefings/events-loop.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# events-on-sx loop agent (single agent, phase-ordered)
|
||||
|
||||
Role: iterates `plans/events-on-sx.md` forever. **Calendar + ticketing as
|
||||
rule evaluation on Datalog** — events / availability / capacity as facts +
|
||||
rules; recurrence expands to occurrence facts within a window; bookings are
|
||||
transactions; reminders/digests are durable `flow`s over an injected
|
||||
notification transport. Pairs with `commerce-on-sx` for paid tickets.
|
||||
|
||||
```
|
||||
description: events-on-sx phase loop
|
||||
subagent_type: general-purpose
|
||||
run_in_background: true
|
||||
isolation: worktree
|
||||
```
|
||||
|
||||
## Prompt
|
||||
|
||||
You are the sole background agent working `/root/rose-ash-loops/events/plans/events-on-sx.md`.
|
||||
Isolated worktree, forever, one commit per feature. Push to
|
||||
`origin/loops/events` after every commit. Never `main`, never `architecture`.
|
||||
|
||||
## Restart baseline — check before iterating
|
||||
|
||||
1. Read `plans/events-on-sx.md` — Phase queue + Progress log + Blockers.
|
||||
2. `ls lib/events/` — pick up from the most advanced file.
|
||||
3. If `lib/events/tests/*.sx` exist, run them via
|
||||
`bash lib/events/conformance.sh`. Green before new work.
|
||||
4. Read `lib/datalog/datalog.sx` public API once — that's your engine.
|
||||
5. Check substrate readiness:
|
||||
- `bash lib/datalog/conformance.sh` — must be green
|
||||
- `lib/persist/persist.sx` — if missing, Phase 2 transactional booking
|
||||
is blocked (note in Blockers; Phase 1 can still proceed without it)
|
||||
- `lib/flow/flow.sx` — if missing, Phase 3 notification flows blocked
|
||||
|
||||
## The queue
|
||||
|
||||
Phase order per `plans/events-on-sx.md`:
|
||||
|
||||
- **Phase 1** — calendar facts + RRULE expansion + availability rules
|
||||
- **Phase 2** — transactional booking (capacity-safe, persist-backed)
|
||||
- **Phase 3** — notification delivery flows (reminders, digests, retry)
|
||||
- **Phase 4** — federation: cross-instance calendars
|
||||
|
||||
Within a phase, pick the checkbox that unlocks the most tests per effort.
|
||||
|
||||
Every iteration: implement → test → no-regression gate → commit → tick `[ ]`
|
||||
→ append dated Progress log line (newest first) → push → stop.
|
||||
|
||||
## Ground rules (hard)
|
||||
|
||||
- **Scope:** only `lib/events/**` and `plans/events-on-sx.md`. Do NOT edit
|
||||
`spec/`, `hosts/`, `shared/`, `lib/datalog/`, `lib/persist/`, `lib/flow/`,
|
||||
`lib/stdlib.sx`, or `lib/` root. May **import** from `lib/datalog/`, and
|
||||
once they exist `lib/persist/` + `lib/flow/`.
|
||||
- **NEVER call `sx_build`.** 600s watchdog. If sx_server binary broken →
|
||||
Blockers entry, stop.
|
||||
- **Capacity safety is the contract.** Two concurrent bookings on the last
|
||||
seat must NEVER both succeed. Test the race explicitly. The
|
||||
capacity-check + append-event must be atomic at the persist boundary; if
|
||||
persist doesn't expose that primitive, that's a substrate Blockers entry,
|
||||
not a workaround.
|
||||
- **Recurrence is bounded.** Never expand an RRULE without a window. An
|
||||
unbounded RRULE expansion is an infinite computation. Every API entry
|
||||
that takes an event takes a (start, end) window too.
|
||||
- **Notifications are at-least-once.** Idempotency keys on every digest /
|
||||
reminder. A retry must not double-deliver a sent message; the recipient
|
||||
shouldn't see two "your event starts in 1h" pings.
|
||||
- **Shared-substrate issues** (problem in datalog / persist / flow) →
|
||||
Blockers entry with minimal repro. Do NOT patch around it.
|
||||
- **SX files:** `sx-tree` MCP tools ONLY. `sx_validate` after edits.
|
||||
- **Worktree:** commit, push to `origin/loops/events`. Never touch `main`
|
||||
or `architecture`.
|
||||
- **Commit granularity:** one feature per commit. Short factual messages
|
||||
(`events: weekly RRULE expansion + 14 tests`).
|
||||
- **Plan file:** update Progress log + tick boxes every commit.
|
||||
|
||||
## Events-specific gotchas
|
||||
|
||||
- **RRULE is well-trodden.** RFC 5545 defines the grammar; don't invent a
|
||||
new recurrence DSL. Implement the subset rose-ash actually needs (daily,
|
||||
weekly, monthly with byday, until/count) and explicitly defer the rest.
|
||||
- **Availability is constraint propagation.** Free/busy = forward-chained
|
||||
Datalog: occurrence facts within window + booking facts + per-attendee
|
||||
conflict rules. The same query answers "is X free?" and "when is X next
|
||||
free?" (same rules, different bindings).
|
||||
- **Capacity ≠ availability.** Availability is per-actor; capacity is
|
||||
per-event. A room with 50 seats has 50 capacity; the room "available" to
|
||||
the 51st booker is `false` even though the room exists. Encode capacity
|
||||
as a separate fact, not as an attribute of availability.
|
||||
- **Notification delivery is shared with feed/notify.** Don't build a
|
||||
bespoke email/push transport — inject one and design the interface
|
||||
generically. When `feed-on-sx` lands its notify path, the two consumers
|
||||
should share a transport. Flag in Progress log when you build a shape
|
||||
that's a candidate for `delivery-on-sx` extraction.
|
||||
- **Paid tickets cross subsystems.** A paid booking calls into
|
||||
`commerce-on-sx` checkout flow. Don't import commerce here — define the
|
||||
contract (request, callback shape) and have commerce import you (or both
|
||||
import a contract module).
|
||||
|
||||
## General gotchas (all loops)
|
||||
|
||||
- SX `do` = R7RS iteration. Use `begin` for multi-expr sequences.
|
||||
- `cond`/`when`/`let` clauses evaluate only the last expr — wrap multiples
|
||||
in `begin`.
|
||||
- `env-bind!` creates a binding; `env-set!` mutates an existing one (walks
|
||||
scope chain).
|
||||
- `sx_validate` after every structural edit.
|
||||
- `list?` returns false on raw JS Arrays — host data must be SX-converted.
|
||||
|
||||
## Style
|
||||
|
||||
- No comments in `.sx` unless non-obvious.
|
||||
- No new planning docs — update `plans/events-on-sx.md` inline.
|
||||
- Short, factual commit messages.
|
||||
- One feature per iteration. Commit. Log. Push. Next.
|
||||
|
||||
Go. Start by reading the plan; find the first unchecked `[ ]`; implement it.
|
||||
128
plans/agent-briefings/identity-loop.md
Normal file
128
plans/agent-briefings/identity-loop.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# identity-on-sx loop agent (single agent, phase-ordered)
|
||||
|
||||
Role: iterates `plans/identity-on-sx.md` forever. **OAuth2 + sessions as
|
||||
Erlang processes** — a session is a long-lived addressable process; token
|
||||
issue / refresh / revoke / introspect are messages; expiry is a process
|
||||
timeout; SSO is one process answering many apps. Pairs with `acl-on-sx`:
|
||||
identity proves "who is X"; acl decides "may X do Y".
|
||||
|
||||
```
|
||||
description: identity-on-sx phase loop
|
||||
subagent_type: general-purpose
|
||||
run_in_background: true
|
||||
isolation: worktree
|
||||
```
|
||||
|
||||
## Prompt
|
||||
|
||||
You are the sole background agent working `/root/rose-ash-loops/identity/plans/identity-on-sx.md`.
|
||||
Isolated worktree, forever, one commit per feature. Push to
|
||||
`origin/loops/identity` after every commit. Never `main`, never
|
||||
`architecture`.
|
||||
|
||||
## Restart baseline — check before iterating
|
||||
|
||||
1. Read `plans/identity-on-sx.md` — Phase queue + Progress log + Blockers.
|
||||
2. `ls lib/identity/` — pick up from the most advanced file.
|
||||
3. If `lib/identity/tests/*.sx` exist, run them via
|
||||
`bash lib/identity/conformance.sh`. Green before new work.
|
||||
4. Read `lib/erlang/runtime.sx` public API once — that's your process
|
||||
substrate.
|
||||
5. Check substrate readiness:
|
||||
- `bash lib/erlang/conformance.sh` — must be green
|
||||
- `lib/persist/persist.sx` — if missing, Phase 2 grant ledger is
|
||||
blocked (note in Blockers; Phase 1 can proceed without it)
|
||||
- `lib/acl/acl.sx` — if missing, Phase 3 grant-checking is blocked
|
||||
|
||||
## The queue
|
||||
|
||||
Phase order per `plans/identity-on-sx.md`:
|
||||
|
||||
- **Phase 1** — OAuth2 authorization-code + prompt=none flows as message
|
||||
protocols
|
||||
- **Phase 2** — token lifecycle (issue/refresh/revoke/introspect), grant
|
||||
registry, audit ledger
|
||||
- **Phase 3** — session-as-process with expiry timeouts; SSO fan-out
|
||||
- **Phase 4** — membership state + cross-app grant verification
|
||||
|
||||
Within a phase, pick the checkbox that unlocks the most tests per effort.
|
||||
|
||||
Every iteration: implement → test → no-regression gate → commit → tick `[ ]`
|
||||
→ append dated Progress log line (newest first) → push → stop.
|
||||
|
||||
## Ground rules (hard)
|
||||
|
||||
- **Scope:** only `lib/identity/**` and `plans/identity-on-sx.md`. Do NOT
|
||||
edit `spec/`, `hosts/`, `shared/`, `lib/erlang/`, `lib/persist/`,
|
||||
`lib/acl/`, `lib/stdlib.sx`, or `lib/` root. May **import** from
|
||||
`lib/erlang/`, and once they exist `lib/persist/` + `lib/acl/`.
|
||||
- **NEVER call `sx_build`.** 600s watchdog. If sx_server binary broken →
|
||||
Blockers entry, stop.
|
||||
- **Revocation must be real.** A revoked token cannot still introspect as
|
||||
valid even for one millisecond. Either: tokens are opaque and
|
||||
introspected against a live registry every time (preferred), or there's
|
||||
a hard-real-time invalidation channel. Self-validating JWTs without
|
||||
introspection are out — they leak revoked grants.
|
||||
- **Authorization is somewhere else.** identity DOES NOT decide
|
||||
permissions. It proves who. Every "is this allowed?" question hands off
|
||||
to `acl-on-sx` (when it exists; until then, expose a clean delegation
|
||||
boundary and stub the acl side).
|
||||
- **Negative answers are explicit.** "Not authenticated" is a state. "I
|
||||
don't know who you are" is a 401, not a 500, not a "well I guess so."
|
||||
Tests cover the negative path as much as the happy path.
|
||||
- **Audit everything that changes a grant.** Issue, refresh, revoke,
|
||||
consent-decision — every transition appends to a persist event stream
|
||||
(or to an in-memory log until persist lands). The ledger is queryable.
|
||||
- **Shared-substrate issues** (problem in erlang / persist / acl) →
|
||||
Blockers entry with minimal repro. Do NOT patch around it.
|
||||
- **SX files:** `sx-tree` MCP tools ONLY. `sx_validate` after edits.
|
||||
- **Worktree:** commit, push to `origin/loops/identity`. Never touch `main`
|
||||
or `architecture`.
|
||||
- **Commit granularity:** one feature per commit. Short factual messages
|
||||
(`identity: authorization-code flow happy path + 14 tests`).
|
||||
- **Plan file:** update Progress log + tick boxes every commit.
|
||||
|
||||
## Identity-specific gotchas
|
||||
|
||||
- **OAuth2 is a state machine, not a function.** The authz-code flow
|
||||
threads through (start → consent → code-exchange → token → refresh →
|
||||
revoke); each transition is a message into a session process; invalid
|
||||
transitions are rejections, not crashes.
|
||||
- **Silent SSO (`prompt=none`) is a fast-path through the same machine.**
|
||||
Don't duplicate the implementation. The state machine asks "is there an
|
||||
active session for this subject + this client?"; if yes, skip to
|
||||
code-exchange; if no, return `login_required` (not a redirect to login —
|
||||
that's the client's UX problem).
|
||||
- **Token storage is the registry, not the token.** Tokens are opaque
|
||||
binaries; the registry is the source of truth. `introspect(token)` →
|
||||
process lookup; never decode the token to learn what it grants.
|
||||
- **Session expiry is a timeout, not a cron.** Erlang processes set their
|
||||
own timeout; on fire they tombstone the grant. Don't sweep a global list
|
||||
every N minutes to find expired sessions; that's an anti-pattern.
|
||||
- **Per-app first-party cookies are an HTTP-layer concern, not core.**
|
||||
Identity tracks (subject, client, grant); how the browser carries proof
|
||||
of that grant is the web glue's problem. Don't tangle cookie SameSite
|
||||
policy into the grant machine.
|
||||
- **OAuth2 is harder than it looks.** Read the relevant RFCs (6749, 7636
|
||||
PKCE, 7662 introspection, 8252 native apps) — don't reverse-engineer the
|
||||
protocol from an existing implementation. Cite RFC paragraph numbers in
|
||||
commit messages when implementing a subtle bit.
|
||||
|
||||
## General gotchas (all loops)
|
||||
|
||||
- SX `do` = R7RS iteration. Use `begin` for multi-expr sequences.
|
||||
- `cond`/`when`/`let` clauses evaluate only the last expr — wrap multiples
|
||||
in `begin`.
|
||||
- `env-bind!` creates a binding; `env-set!` mutates an existing one (walks
|
||||
scope chain).
|
||||
- `sx_validate` after every structural edit.
|
||||
- `list?` returns false on raw JS Arrays — host data must be SX-converted.
|
||||
|
||||
## Style
|
||||
|
||||
- No comments in `.sx` unless non-obvious.
|
||||
- No new planning docs — update `plans/identity-on-sx.md` inline.
|
||||
- Short, factual commit messages.
|
||||
- One feature per iteration. Commit. Log. Push. Next.
|
||||
|
||||
Go. Start by reading the plan; find the first unchecked `[ ]`; implement it.
|
||||
Reference in New Issue
Block a user