Repository navigation
blog: every post carries a widget, as a t27 rule the work report enforces (Closes #1576) - #1577
Conversation
…rces (Closes #1576) The owner's rule of 2026-10-09 now lives in apps/website/specs/policy/post_widget.t27. A post body that a PR adds, modifies or renames holds at least one terminal cast or figure in its English body and in its Russian ruBody. 72 older widgetless posts dated before 2026-09-27 are grandfathered by name. The spec has 5 tests, and t27c test-report counts 0 vacuous passes. pr-blog-report.yml saves each changed body as text from the PR head. pr_blog_report.py validate --posts reads the spec's constants and refuses a body without a widget. The workflow never runs that text. Foreign glue: 40 + 32 + 8 = 80 added lines. The 14 widgetless posts dated on or after 2026-09-27 get existing casts that match their text, in EN and RU. mask-over-scale has no matching recording, so it gets a figure built from its own numbers. The clocks post said one MMCM test fails; its recording shows two, so that sentence is corrected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Summary by t27-beesChecked by the t27 compilert27c parse, parse-complete and typecheck on each changed .t27 file at
Read by the modelThis PR codifies the owner's rule that every t27.ai blog post dated on or after 2026-09-27 must carry a widget (terminal cast or figure) in both English and Russian bodies. It adds a t27 spec ( Worth a look
Read: 19 files, +512 -3, the whole diff up to 60000 characters (the rest was not read). Head |
X Brain Health CheckScore: 100.0/100 X Brain is above merge threshold |
blog: every post carries a widget, as a t27 rule the work report enforces (Closes #1576) (#1577) The owner's rule of 2026-10-09 now lives in apps/website/specs/policy/post_widget.t27. A post body that a PR adds, modifies or renames holds at least one terminal cast or figure in its English body and in its Russian ruBody. 72 older widgetless posts dated before 2026-09-27 are grandfathered by name. The spec has 5 tests, and t27c test-report counts 0 vacuous passes. pr-blog-report.yml saves each changed body as text from the PR head. pr_blog_report.py validate --posts reads the spec's constants and refuses a body without a widget. The workflow never runs that text. Foreign glue: 40 + 32 + 8 = 80 added lines. The 14 widgetless posts dated on or after 2026-09-27 get existing casts that match their text, in EN and RU. mask-over-scale has no matching recording, so it gets a figure built from its own numbers. The clocks post said one MMCM test fails; its recording shows two, so that sentence is corrected. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
🧪 Brain Stress Test ResultsTests: 261/261 passed, 0 leaked |
…1579) (#1580) * ci(project-status): read the PR body from the environment (Closes #1579) The Move-closed-issues step pasted ${{ github.event.pull_request.body }} into the script text, so a quote in a PR body broke the step (#1577) and shell in a PR body ran with PROJECT_TOKEN in the environment. The body now reaches bash only as the PR_BODY variable, as dev-enforcement.yml already does for the title. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci(agent-complete): read the branch name from the environment (Closes #1579) The Extract-issue-number step pasted ${{ github.head_ref }} into the script. A branch name may hold quotes, $ and parentheses, and the step writes GITHUB_OUTPUT, which the next step (with RAILWAY_API_TOKEN) reads. It now reaches bash only as HEAD_REF. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
Closes #1576
The owner's rule of 2026-10-09, that every t27.ai post carries a widget, is now a t27 spec, and the required
T27 work reportcheck enforces it on every PR that adds or changes a post. The 14 posts that broke it now have widgets.The rule:
apps/website/specs/policy/post_widget.t27, next tocourse_post.t27and in the same style.WIDGET_KINDS = ["terminal", "figure"], the cutoffWIDGET_SINCE = "2026-09-27", andGRANDFATHERED: the 72 posts dated before the cutoff that carry no widget, listed by name.testblocks.t27c test-report(0.5.2) passes 5 of 5 with 0 vacuous passes.The glue stays at the Only-t27 cap: 40 + 32 + 8 = 80 added foreign lines.
pr-blog-report.ymlsaves each changed post body as text from the PR head, through the contents API. It never checks out or runs the PR.scripts/pr_blog_report.py validate --postsreads the spec's constants with the same regexes ascourse_post.t27. It refuses a body that has no widget inbodyorruBody, and fails closed when the spec is broken or a body was not saved.docs/PR_BLOG_AUTOMATION.mdgets a short section that points at the spec.Posts and casts. Every cast already exists under
public/term/. I read each cast's text before writing its caption, and the Russian body gets the same blocks.One text fix: the clocks post said one MMCM test fails when the VCO floor is lowered. Its recording shows two,
vco_below_the_window_is_rejectedandvco_out_of_range_is_an_error, so the sentence now says two, in both languages.{ "version": 1, "head_sha": "b4d6e9ffdc9c79b3084e49bdae5a56669e733dbf", "summary": "The owner's rule that every t27.ai post carries a widget is now a t27 spec, post_widget.t27. The required work-report check enforces it on changed post bodies, and the 14 widgetless posts dated on or after 2026-09-27 get casts or a figure, in English and Russian.", "changes": [ "apps/website/specs/policy/post_widget.t27: the rule sentence, WIDGET_KINDS terminal and figure, WIDGET_SINCE 2026-09-27, 72 grandfathered post names, and 5 test blocks.", "scripts/pr_blog_report.py: post_widget_policy reads the spec's constants and fails closed; post_widget_refusal refuses an added, modified or renamed body with no widget in body or ruBody; validate takes --posts (40 added lines).", "scripts/test_pr_blog_report.py: 3 tests that cover the real spec, a broken spec, both languages, grandfathered, removed and unsaved bodies (32 added lines).", ".github/workflows/pr-blog-report.yml: a step saves each changed post body from the PR head as text through the contents API, and validate gets --posts (8 added lines).", "apps/website/src/data/blog/bodies/*.ts: 14 posts get existing terminal casts (one gets a figure) in English and Russian, and the clocks post's MMCM sentence is corrected from one failing test to two.", "docs/PR_BLOG_AUTOMATION.md: a short section that points at the spec." ], "tests": [ { "command": "t27c test-report apps/website/specs/policy/post_widget.t27 (t27c 0.5.2, zig 0.16.0)", "status": "passed", "result": "5 of 5 pass, 0 vacuous; WIDGET_KINDS[1] changed to chart fails a_widget_is_a_cast_or_a_figure, and a changed last grandfathered name fails the_grandfathered_list_is_closed", "evidence": "local run on this head, 2026-10-09" }, { "command": "python3 -m unittest discover -s scripts -p 'test_pr_blog*.py'", "status": "passed", "result": "74 tests OK; five planted bugs in the new glue (ruBody skipped, grandfathering off, length check off, removed bodies checked, wrong segment matched) each fail one test", "evidence": "local run on this head, 2026-10-09" }, { "command": "post_widget_refusal over every body file, main against this branch", "status": "passed", "result": "on main's 98 bodies it refuses exactly 14, the 11 named in the issue plus 3 from 09-29 to 10-02; on this branch it refuses 0, with 72 grandfathered", "evidence": "local run on this head, 2026-10-09" }, { "command": "validate --posts on trinity#1569's real files and head (the fetch step simulated with gh api)", "status": "passed", "result": "exit 1, refused: three-dies-one-verdict.ts has no widget in body or ruBody; without --posts it exits 0 as before", "evidence": "local run, 2026-10-09" }, { "command": "esbuild bundle of src/data/blog/posts.ts, widget count per post dated on or after 2026-09-27, and a check that each terminal src exists under public/", "status": "passed", "result": "23 posts, every one has at least one widget in both languages, the same number in each, and 0 missing recordings", "evidence": "local run on this head, 2026-10-09" }, { "command": "npm run typecheck:ratchet and npx vite build in apps/website", "status": "passed", "result": "179 errors across 26 files, the same as the baseline; the build finished in 25 s", "evidence": "local run on this head, 2026-10-09" }, { "command": "vite preview, #/blog/three-dies-one-verdict and #/blog/mask-over-scale-ternary-drafts-that-compile in a browser", "status": "passed", "result": "the tri-fpga-jtag player mounts and types the recording; the figure renders four bars with its caption", "evidence": "local run on this head, 2026-10-09" }, { "command": "the website-checks gate on this PR", "status": "not_run", "result": "not run locally beyond typecheck and build", "evidence": "the website-checks workflow on this pull request runs it" } ], "limitations": [ "The new rule runs from main (pull_request_target), so it binds PRs after this one merges, not this PR.", "The PR-time check cannot see whether a terminal block's recording exists, because the workflow never checks out the PR; the site's player shows a missing one.", "Widgets are matched by the text kind: 'terminal' or \"kind\": \"figure\"; a block built by a function call would not be seen.", "72 older posts stay without widgets, by name; the list only shrinks.", "Owner rule says each post also needs its og-art triptych; this PR does not touch triptychs.", "t27c cannot lower != on two str values yet (Zig: operator != not allowed for []const u8), so the spec does not assert that the two widget kinds differ." ], "tags": [ "Blog", "Policy", "t27" ], "blog": { "title": "Every post carries a widget", "summary": "A rule that was said once and written nowhere let 11 posts ship as text only. It is now a t27 spec, and the required check enforces it on every post a PR changes.", "outline": [ "Eleven posts in a row shipped with no recording, because the widget rule lived only in a conversation.", "post_widget.t27 holds the rule, the two widget kinds and the grandfathered posts, and pr_blog_report.py reads it on every PR.", "Fourteen posts got casts that show the command they discuss; one got a figure, and one caption corrected its own post." ] } }🤖 Generated with Claude Code