Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions .trinity/seals/automation_automation::course_access.json
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
{
"built_by": "t27c-bootstrap@0.5.2+d2c8e7d41",
"built_by": "t27c-bootstrap@0.5.2+239b4b059",
"closure": [],
"config": "gen=c,rust,verilog,zig;tests=zig",
"gen_hash_c": "sha256:0724a34f57d23c9924b871a392774c7a19f957876fd60045f6e627e32c4db2f2",
"gen_hash_rust": "sha256:52e27f4fece6b2c73d5c1086b3b33a648af434a9451c732d522e742337de6838",
"gen_hash_verilog": "sha256:7afb008af0e4a2e5e4ced3e5acf0d2c41b99ea72c7f2090f159c18e4cf8fb82e",
"gen_hash_zig": "sha256:6dc8d990374d50fdae0e7ba44161f7c23fe8fbbc3a48a2bbb1b1d0362b3d663f",
"gen_hash_c": "sha256:a44fa8733f70d5560f2443f5151341d100273210f6311940e5a3918e04ce6535",
"gen_hash_rust": "sha256:9780e1a4766e8590ab9496771900236e1472d8d7b8a8a5938f82b93fb96ca7b2",
"gen_hash_verilog": "sha256:d448bc2dfdf9ad7860a499e394bbf3e7946d65072cb60181c719e9c202584716",
"gen_hash_zig": "sha256:c6a00ec44d6a4198d10d7d2492f4bd5b7c6964cda26eb72a4366734be029a1b7",
"module": "automation::course_access",
"ring": 12,
"seal_schema": 2,
"sealed_at": "2026-10-18T05:03:39Z",
"sealed_at": "2026-10-19T03:00:14Z",
"sealed_by": "t27c-bootstrap@0.5.2",
"spec_hash": "sha256:89b4c6d4a9a8f0eb7534d0b9ee98af631cabc474e964e0dcb4b0233afdef09e4",
"spec_hash": "sha256:edd87ade1cc387f4e153bf22ce4c51856178377e4b4434e679ba7539ba39b978",
"spec_path": "specs/automation/course-access.t27",
"tests": {
"failed": 0,
"failing": [],
"forced": false,
"passed": 16,
"total": 16,
"passed": 18,
"total": 18,
"vacuous": 0
},
"toolchain": {
"test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d",
"test_runner": "sha256:8512b80da533b79289110fca4a65f3336abd7a059ca4b72ea595d5e33ff8ad10",
"zig": "0.16.0"
}
}
11 changes: 11 additions & 0 deletions docs/now/2026-10-19-course-access-v3-every-course-free.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# NOW -- course-access VERSION 3: every course free for everyone (2026-10-19)

## specs/automation/course-access.t27 (Closes #10798)

- Why: owner, 2026-10-19 -- "make the courses free for everyone! our task is to grow the language". A course behind the club is a reader the language does not get; the club stays the bot's own subscription for generations.
- `step_of(opened)` returns `STEP_FREE` for every rung (v2: free for three, then paid). `LADDER_RUNGS` counts the rungs that differ before `step_of` repeats its last: 4 in v2, 1 now.
- `STEP_BOT`, `STEP_PAID`, `may_open`, `stays_open`, `mint_ok`, `key_days` unchanged: a key already minted keeps opening what it opened. `lesson_event`, `tells_owner`, `may_report` do not depend on the paid rung and are unchanged; `SITE_CHECKOUT` and `CONTENT_HIDDEN` stay false.
- Tests: the old paid-ladder asserts now assert free; a new loop pins `step_of(n) == STEP_FREE` for n = 0..255 (767 runtime asserts); a new test keeps a held paid key opening a paid step. VERSION 3 with a dated header note and its test.
- t27c (bootstrap code unchanged since 239b4b059), zig 0.16.0: test-report 18 of 18 pass, 0 vacuous. Negative control: the v2 `step_of` body turns 3 tests red.
- `scripts/t27-logic.mjs` (999) lowers 9 fns, 0 not; in node `stepOf(1..10)` is all 0. Seal saved.
- Not here: re-vendoring into 999 render and trinity (`courseLadder.ts`). A course stored on a paid rung under v2 still closes by `stays_open` when the club ends; a host that must keep it open re-rungs it with `step_of`.
79 changes: 56 additions & 23 deletions specs/automation/course-access.t27
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// specs/automation/course-access.t27 -- the ladder to the t27 courses: the reader picks
// any three courses and they open free, the fourth and every one after it need the paid
// club; every reader who reaches the bot is filed in the owner's CRM, and the owner is
// told, live, each step they take.
// specs/automation/course-access.t27 -- the ladder to the t27 courses: since VERSION 3
// every course opens free, for every reader, with one press; every reader who reaches
// the bot is filed in the owner's CRM, and the owner is told, live, each step they take.
// Host: 999-multibots-telegraf
// apps/vibee-editor/render/src/agent/course-access.ts (the key: mint, check; the
// learner row; the CRM person, tag and touch; the owner's live message) and the routes in
Expand All @@ -18,11 +17,12 @@
// THE LADDER. The reader chooses which course is which rung: the rungs are steps, not
// courses. A step is FREE (one press, nothing asked), BOT (the reader opens the
// bot with a course link; the bot hands back a key) or PAID (the bot checks the
// club and hands back a key that lives as long as the club does). Rungs past the
// fourth stay PAID. A course opened on a free or a bot rung stays open for that
// reader; one opened on a paid rung stays open only while he holds a paid key that
// has not run out (stays_open): the club is a subscription, not a purchase. The
// order is kept in his browser, the keys prove the two steps that need proof.
// club and hands back a key that lives as long as the club does). Which step a
// rung is, is step_of's; past LADDER_RUNGS it repeats the last. A course opened
// on a free or a bot rung stays open for that reader; one opened on a paid rung
// stays open only while he holds a paid key that has not run out (stays_open):
// the club is a subscription, not a purchase. The order is kept in his browser,
// the keys prove the two steps that need proof.
// THE KEY. Signed by the render with a secret only the render holds; it carries the
// step, an opaque learner id and an expiry, never a Telegram id or a name. A BOT
// key does not expire; a PAID key expires with the club period it was minted
Expand Down Expand Up @@ -56,6 +56,21 @@
// one after it PAID. STEP_BOT stays a step the render mints and may_open and
// stays_open still honour, so a course already opened with a bot key stays open.
// Paying on the site is still not built (SITE_CHECKOUT).
// VERSION 3, owner, 2026-10-19 -- "make the courses free for everyone! our task is to
// grow the language". A course that asks for the club is a reader the language
// does not get, so step_of returns FREE for every rung: no course needs the club
// or the bot. The club stays the bot's own subscription, for generations; it is no
// longer a door to a course. LADDER_RUNGS counts the rungs that differ before
// step_of repeats its last one: four in VERSION 2 (free, free, free, paid, then
// paid for good), one now (free, then free for good). STEP_BOT and STEP_PAID stay
// steps the render mints and may_open, stays_open, mint_ok and key_days still
// honour, so a key already minted keeps opening what it opened. What this leaves
// to the hosts: a course a reader stored on a paid rung under VERSION 2 is judged
// by stays_open with its stored step and closes when his club ends; a host that
// must keep it open re-rungs it with step_of. With no rung asking for the bot, a
// reader is filed in the CRM and reports lessons only if he opens the bot himself;
// the site still sends nothing before that (SENDS_BEFORE_BOT). SITE_CHECKOUT stays
// false: there is no checkout, and no course needs one.
// Claim status: written before any live run; no key has been minted, no reader filed.
// phi^2 + 1/phi^2 = 3 | TRINITY

Expand All @@ -64,13 +79,13 @@ module automation::course_access {
pub const KIND : str = "automation";
pub const ID : str = "course-access";
pub const REPO : str = "999-multibots-telegraf";
pub const VERSION : u8 = 2;
pub const VERSION : u8 = 3;

// ---- The steps ---------------------------------------------------------------------
pub const STEP_FREE : u8 = 0;
pub const STEP_BOT : u8 = 1;
pub const STEP_PAID : u8 = 2;
pub const LADDER_RUNGS : u8 = 4; // free, free, free, paid; every rung after it is paid
pub const LADDER_RUNGS : u8 = 1; // one rung, free; every course after it is free too
pub const COURSES_KNOWN : u8 = 7; // the catalog today; the ladder does not depend on it

// ---- The bot link ------------------------------------------------------------------
Expand Down Expand Up @@ -102,11 +117,9 @@ module automation::course_access {
pub const RUN_LIVE : bool = false;

// Which step the next course is, given how many this reader has opened already.
// No rung is BOT since VERSION 2: the bot step was where readers stopped.
// No rung is BOT since VERSION 2 (the bot step was where readers stopped) and no rung
// is PAID since VERSION 3: every course is free, to grow the language.
fn step_of(opened: u8) -> u8 {
if opened >= 3 {
return STEP_PAID;
}
return STEP_FREE;
}

Expand Down Expand Up @@ -231,14 +244,33 @@ module automation::course_access {
assert(may_open(STEP_BOT, true, false));
}

test "the fourth and every one after it need the club" {
assert(step_of(3) == STEP_PAID);
assert(step_of(4) == STEP_PAID);
assert(step_of(6) == STEP_PAID);
assert(step_of(200) == STEP_PAID);
test "the fourth and every one after it are free too, no club asked" {
assert(step_of(3) == STEP_FREE);
assert(step_of(4) == STEP_FREE);
assert(step_of(6) == STEP_FREE);
assert(step_of(200) == STEP_FREE);
assert(may_open(step_of(3), false, false));
assert(may_open(step_of(200), false, false));
assert(step_of(3) != STEP_PAID);
assert(LADDER_RUNGS == 1);
}

test "every course is free, for every reader, however many he has opened" {
var n : u8 = 0;
while (n < 255) {
assert(step_of(n) == STEP_FREE);
assert(step_of(n) == step_of(LADDER_RUNGS - 1));
assert(may_open(step_of(n), false, false));
n += 1;
}
assert(step_of(255) == STEP_FREE);
assert(may_open(step_of(255), false, false));
}

test "a paid key a reader already holds still opens a paid step" {
assert(!may_open(STEP_PAID, false, false));
assert(!may_open(STEP_PAID, true, false));
assert(may_open(STEP_PAID, false, true));
assert(LADDER_RUNGS == 4);
}

test "a club course closes when the club ends, the others stay" {
Expand Down Expand Up @@ -327,9 +359,10 @@ module automation::course_access {
}

test "VERSION says what the header's last note says" {
assert(VERSION == 2);
assert(VERSION == 3);
assert(step_of(VERSION) == STEP_FREE);
assert(step_of(VERSION + 1) == STEP_PAID);
assert(step_of(VERSION + 1) == STEP_FREE);
assert(step_of(VERSION + 1) != STEP_PAID);
}
}
// phi^2 + 1/phi^2 = 3 | TRINITY
Loading