50 descendants and 50 ancestors of 645b11cd8a11a47a
|
2026-08-29
| ||
| 01:14 |
settings: every panel confirms the same way, in the palette's green
The description form got a Save/Saved swap; the other panels still said nothing at all. Worse, three of them redirected to ?saved=1, which had quietly stopped meaning anything when the flash cookie replaced it — a regression I left behind an hour ago. Six exit paths across five handlers were still writing it. They all set a named flash now. The name matters: a page carries six Save buttons, and a bare boolean would light every one of them for a single save, telling the reader that everything was written when one thing was. Green comes from --add, the token that already marks a passing run and an open glyph, so 'it worked' is one colour across the forge. Webhooks is deliberately untouched: it is a LIST with Save, Pause and Delete per row, so lighting 'the' Save would need to know which row. Different shape, not this pattern. The visibility test pinned ?saved=1; it now asserts the flash, which is the contract rather than the spelling of a mechanism. check-in: 2f0c4fcb50 user: dmestas tags: trunk | |
| 01:09 | ci: verdict tags opt-in per repo; premerge verdicts carry onto the merge check-in: d36be0ef02 user: dmestas tags: ci-5 | |
| 01:04 | ci: premerge — checkout helper, pair-keyed runs, per-branch mode closed check-in: 2dfd42319f user: dmestas tags: ci-4 | |
| 01:01 |
settings: the save confirms on the button, not in the page
The inline word had to appear and then leave, which is two layout shifts around the control just pressed. On the button there are none: both labels share one grid cell, so it is sized by the wider of them and nothing moves when they swap. The revert is CSS on the same four-second beat, because without script the label must still find its way back to Save. The announcement stays — sr-only, role=status — since a screen reader cannot read a button relabelling itself as an event. Reduced motion keeps the word and drops the movement. check-in: d28f1388fe user: dmestas tags: trunk | |
| 00:55 | ci: check.report, reporter tokens, ci.trigger check-in: 28eeb18515 user: dmestas tags: ci-3 | |
| 00:54 |
settings: the save's confirmation has a lifetime, and is spent once
Three faults in one paragraph. It never left: a confirmation is an EVENT, and rendering it as page content claims the page CONTAINS the fact of having been saved, which is only true for a moment. A reload replayed it: ?saved=1 stays in the address bar, so a refresh — or a bookmark — congratulated the reader for a save from hours ago. And it shifted the layout under the button just pressed. A one-shot cookie now carries the word across the redirect and is spent on arrival, so the URL stays clean and the second render is silent. The lifetime is CSS: it holds four seconds, then fades AND collapses, so no empty paragraph is left behind. Not script — this page must behave the same without JavaScript, and an animation costs nothing when the script is absent. role=status announces it on insert, before any of that runs. Reduced motion keeps the message and drops the animation: someone asking for less movement is not asking to be told less. check-in: 563eb933dd user: dmestas tags: trunk | |
| 00:45 | drop unrelated formatting churn from three web files check-in: 67a13adccf user: dmestas tags: ci-2 | |
| 00:44 | ci: checks panel, required checks, override with a reason check-in: 5d0f1de4c6 user: dmestas tags: ci-2 | |
| 00:35 |
settings: save the description in place when Datastar is present
The form keeps its method and action, so without the script it posts and redirects exactly as before — the data-on:submit attribute is inert (0001 Principle 3). With the script it posts to the SAME endpoint, which answers a Datastar-Request with a patch of the panel instead of a page: the same discrimination the board's move already uses. The panel is one template block, drawn by the page and re-drawn by the save, so the two cannot drift — the fault this page had an hour ago. Its 'Saved.' now sits beside the control that saved, not at the top of a page the reader has scrolled away from, and the refusal travels with the panel as PanelError rather than as the page-wide Error. The URL is deliberately left alone on the patched path. ?saved=1 exists to carry a word across a navigation; there is no navigation here, and putting it in the address bar would make a later reload announce a save that already happened. check-in: bbd1663825 user: dmestas tags: trunk | |
| 00:26 | rfc 0009: CI — verdicts on landings, spec, premerge, runners closed check-in: 56d87626d9 user: dmestas tags: ci-rfc | |
| 00:07 |
settings: saving no longer empties the page
The description form rendered the settings page from seven fields of its own while the GET built a dozen panels, so Save returned a page with everything below the form gone — and a stray h1, because the POST also missed HideH1. Two symptoms, one cause: the same page assembled in two places. There is one builder now, and the successful save REDIRECTS, as every other settings POST already did. That leaves the saved state in one place, so the two cannot disagree again and a reload does not re-submit. The refusal path still comes back holding what the reader typed — now over the whole page rather than a fragment of it. Verified: after a save the page is byte-equivalent to the GET but for the Saved flash, with all nine panels present. check-in: d2368f6a92 user: dmestas tags: trunk | |
|
2026-08-28
| ||
| 21:08 |
a segmented control never breaks mid-way; the tools beside it move
Squeezed on a phone, the tracker's state filter dropped its last choice onto a line of its own — '99 All' under '30 Open | 69 Closed' — which reads as a stray button rather than as part of the set. The control now keeps its line whole and comes first, because it is how a reader says what they are looking at; the tool groups beside it wrap instead. The landings list is unchanged: one icon fits beside its filter, so that row still holds one line. check-in: 87f42420b7 user: dmestas tags: trunk | |
| 17:20 | land perm-fixups check-in: 78a6a46d87 user: dmestas tags: trunk | |
| 15:49 |
landings list: clip long titles and drop their markup
A row whose title is the whole message ran fifteen lines down a phone and pushed every other record off the screen. Clipped at the same 72 the timeline uses for this exact string, so the two listings agree on how long a landing's name is. The marks come off too: a title is plain text inside the row's link, so *emphasis* and `code` were printing their delimiters. The detail page still renders the message in full, with its markup — a list says what a record is called, not what it says. check-in: f2d73a8545 user: dmestas tags: trunk | |
| 15:47 | accounts: logins are a closed alphabet closed check-in: 03ca730475 user: dmestas tags: perm-fixups | |
| 15:47 | caps: migration keeps repo_role while any row is unknown check-in: e6d7d4cac3 user: dmestas tags: perm-fixups | |
| 15:46 | api: grant and revoke carry the guard's warning check-in: 460339d1db user: dmestas tags: perm-fixups | |
| 15:45 | web: raw acct- saves meet the lockout guard check-in: 33dbf50ef7 user: dmestas tags: perm-fixups | |
| 15:45 | caps: repo-less effective set resolves stock, not the fleet default check-in: 52ee45d56d user: dmestas tags: perm-fixups | |
| 01:21 |
landings: headings scale instead of clipping; the decision row abbreviates
The h1 had no rule at all, so it drew at the browser's 2em on every screen. A real subject line then became three lines of shouting on a phone, which made clipping look necessary — it was not. The clip only hid words: 'permissions without a…' with the rest nowhere. Headings now clamp between 1.35rem and 2rem and wrap, so a long one fits and says the whole thing. The pathological case — no subject line at all, the title IS the body — already drops the visible heading, so nothing depends on the clip any more. The decision row gains a second step at 22rem: measured at 320, five abbreviated buttons want 364px against 302 of room, and Comment is the widest word left. It has an icon and destroys nothing, so it is the one that loses its label. 'Land the strata (N rungs)' is now 'Land strata (N)' at every width — it was too many words wide as well as narrow. The strata test pinned that sentence, so a wording change read as a missing control. It now asserts the formaction, which is the contract, and the rung count rather than the sentence around it. check-in: 6469f151c4 user: dmestas tags: trunk | |
|
2026-08-27
| ||
| 23:54 |
landings: the decision row stays one line, the words give way instead
The row grew from three verbs to five as landings learned to update and to land a whole strata. Flex items shrink before they overflow, so on a phone every label folded to three lines AND the leftmost button still ran off the side. flex:none and nowrap keep each button one line at its own width; below 30rem the labels shorten — Update, Accept, Land x2 — so all five fit without the row breaking. The long forms stay on wider screens. Reject keeps its word at every width: there is no X in the sprite, and an unlabelled destructive control is worse than a cramped one. check-in: 87482c870f user: dmestas tags: trunk | |
| 21:14 | land perm-8 check-in: 04b136736f user: dmestas tags: trunk | |
| 21:14 | land perm-7 check-in: 913640f9c8 user: dmestas tags: trunk | |
| 21:14 | land perm-6 check-in: a24572d64c user: dmestas tags: trunk | |
| 21:14 | land perm-5 check-in: a2310e6ea0 user: dmestas tags: trunk | |
| 21:13 | land perm-4 check-in: 2800e23b4c user: dmestas tags: trunk | |
| 21:13 | land perm-3 check-in: 04fb645814 user: dmestas tags: trunk | |
| 21:13 | land perm-2 check-in: 31c8a14681 user: dmestas tags: trunk | |
| 21:13 | land perm-1 check-in: cff61f323b user: dmestas tags: trunk | |
| 21:02 | roles: settings/config live via zit -R (zit#153 shipped) closed check-in: 05497a6552 user: dmestas tags: perm-8 | |
| 16:35 | roles: fossil settings and config surface, blocked on zit#153 check-in: afe83bd5e7 user: dmestas tags: perm-8 | |
| 14:37 | roles: Datastar live preview, two-way string and checkboxes closed check-in: ad5348a584 user: dmestas tags: perm-7 | |
| 14:32 | roles: settings IA reorder, Advanced (fossil) with decoders closed check-in: d299bb35af user: dmestas tags: perm-6 | |
| 14:29 | roles: visibility presets, take-private wizard, fleet default closed check-in: 40c3fe1567 user: dmestas tags: perm-5 | |
| 14:19 | roles: People section, per-person editor, account.explain, guard closed check-in: 005d6862c1 user: dmestas tags: perm-4 | |
| 14:12 |
landings: no headline where the author wrote none
A record only HAS a headline when its author wrote one — a subject line, a blank line, then the body. Written without that, the whole message is the title, and clipping it to 72 invented a headline the author never wrote, then ended it in an ellipsis that says data is hidden when the same words are in full directly below. The visible heading now goes for that shape: the chips and the message carry the record, and the sr-only h1 keeps the page named. A record that does have a subject line still shows it. check-in: 2dccc0cb5f user: dmestas tags: trunk | |
| 14:09 | roles: one system, repo_role retired closed check-in: b0e95e0777 user: dmestas tags: perm-3 | |
| 14:02 | roles: the compiler — presets, abilities, closure, infer, decode closed check-in: f5fae58b0c user: dmestas tags: perm-2 | |
| 13:58 | RFC 0008: roles — permissions without a manual closed check-in: 15e36a8c1a user: dmestas tags: perm-1 | |
| 13:18 |
landings: the record shows its whole message, and the heading shows no markup
Two faults from one cause. A message written with no blank line has no prose under the header at all — zit put the whole thing in the title card. So clipping the heading dropped 290 of 362 characters with nowhere left to read them, and the record rendered no description. The opener now falls back to the title, rendered like any other body, which is also the only way its Markdown ever reaches a reader. The heading drops the marks instead: an h1 is plain text, so *emphasis* arrived as literal asterisks and read as a rendering bug. Asterisks and backticks only — _x_ in prose and my_file_name in a path are the same three characters, and mangling every identifier to italicise a rare one is the worse trade. check-in: db743a90a0 user: dmestas tags: trunk | |
| 13:07 |
landings: the heading is a headline, not the whole message
zit puts the entire -m message in the record's title card, so a message written without a subject line arrives as one 400-character 'line' that firstLine returns whole — an h1 that fills a phone screen before the record says anything about itself. Clipped at the same 72 the timeline already uses for this exact string, so the two agree on what a landing is called. Nothing is lost: the opener's full text renders immediately below as the description. check-in: ff1cfd1aa9 user: dmestas tags: trunk | |
| 12:12 | Landed: the disabled state. check-in: d39333b854 user: dmestas tags: trunk | |
| 12:12 | landing: a disabled decision reads as disabled closed check-in: ec0abed3b4 user: dmestas tags: disabled-style | |
| 12:09 | Landed: controls follow the capability that presses them. check-in: a7850eb7a1 user: dmestas tags: trunk | |
| 12:09 | landing: decision controls no longer hide behind the composer closed check-in: e44303dc2c user: dmestas tags: landing-controls | |
| 12:02 | Landed: the contained-branch reading. check-in: 99bed00eb6 user: dmestas tags: trunk | |
| 12:02 | landing: a contained branch reads accepted, not claimed closed check-in: 938c28695e user: dmestas tags: contained-claim | |
| 11:42 | Green against merged trunk: vet, build and the full suite. Clicking a doc tab now moves the address bar to the tab's own href, verified live. check-in: c2ccbb5e93 user: dmestas tags: trunk | |
| 11:42 |
Merge trunk
Current with the segment below, now landed. closed check-in: a50a6dee00 user: dmestas tags: url-sync-doctabs | |
| 11:41 | Green against merged trunk: vet, build and the full suite. Emitted markup unchanged, so the diff toggle behaves exactly as before. check-in: e7d457d6d5 user: dmestas tags: trunk | |
| 11:41 |
Merge trunk
Keeps the segment current with the landing.update work before it lands. closed check-in: 645b11cd8a user: dmestas tags: url-sync-helper | |
| 11:22 | Landed: Update from target, on the store. check-in: f64e1eb73a user: dmestas tags: trunk | |
| 11:22 | landing.update: bring the target into the branch on the store (zit#152) closed check-in: ab5e011fc2 user: dmestas tags: landing-update | |
| 10:52 |
One definition of swap-the-body-and-fix-the-URL
The diff toggle carried the replaceState handler as literal text in two attributes, with the reasoning in a comment above them. Both move onto a syncurl helper, so the next surface that needs it gets the try/catch and the replaceState-not-pushState choice for free instead of by copy-paste. Emitted markup is unchanged. The helper neutralizes quotes in the fragment URL: it lands inside a JS string inside an attribute built as HTMLAttr, where escaping is off. check-in: 058a2216c9 user: dmestas tags: url-sync-helper | |
| 10:08 | Landed: the size cap on what is drawn. check-in: d05ac2958a user: dmestas tags: trunk | |
| 10:08 | Landed: conflict steps; rungs above follow. check-in: 96d8fa7224 user: dmestas tags: trunk | |
| 10:08 | Landed: the top rung's verdict is the stack's. check-in: 57dbf59e99 user: dmestas tags: trunk | |
| 10:08 | Landed: landing.land, whole stack or bottom-N. check-in: d334dbfd92 user: dmestas tags: trunk | |
| 10:08 | Landed: retarget replies read by the fold. check-in: 02bc605852 user: dmestas tags: trunk | |
| 02:53 | strata: past twenty rungs, say how tall rather than draw closed check-in: e1557f4791 user: dmestas tags: strata-cap | |
| 02:51 | landing: the steps for a conflicting rung; rungs above follow closed check-in: dcc39e7343 user: dmestas tags: strata-help | |
| 02:49 | docs: regenerate api.md closed check-in: ca48cce188 user: dmestas tags: strata-verdict | |
| 02:48 | strata: the top rung's verdict is the stack's check-in: f35fe25d54 user: dmestas tags: strata-verdict | |
| 02:48 | landing.land: land a strata up to a rung, bottom-up closed check-in: c3d29faae3 user: dmestas tags: strata-land | |
| 02:48 | landing: read zit#144 retarget replies (carried from in-flight work) closed check-in: f61dfbb349 user: dmestas tags: retarget-reply | |
| 00:04 | Landed: a strata that looks like one thing, because it is one thing. check-in: ca52fa73e4 user: ai-local tags: trunk | |
| 00:03 |
Landings: a strata is one row, and a map on the record
The open queue folds a stack under its base: a strata mark, the segment count, the landable end named, and the rungs in a details element with 1/N badges — no script. Settled views stay flat; a settled segment has left its chain. The record page draws the map: every rung bottom-up, this one marked, the landable one named, a rejected base as a broken rung. Above the bottom the accept control is disabled and says the segment below lands first. Proved with a real zit: two stacked landings count one decision; the top rung's accept is refused; accepting the base retargets the next onto trunk and it becomes the landable end, still one decision. closed check-in: 02f6aadcad user: dmestas tags: strata-ui | |
| 00:03 |
RFC 0006: the strata is derived, and the queue counts decisions
AttachStrata reads the chain from target -> branch across landings: base, position, depth, and why a chain cannot be walked — a cycle, or a rejected base. Derived at read time, never stored, so a segment retargeted by zit#144 after its base lands simply stops being one. landing.list carries the reading and counts DECISIONS: a stack of five is one, the base's, because nothing above it can be acted on. landing.get carries the chain for the record's map and reports a rung above the bottom as stacked, which the merge probe must not overwrite: its answer is true and useless there. check-in: 3e3ad17751 user: dmestas tags: strata-ui | |
| 00:02 |
markup: fossil wiki resolves mentions and refs, like markdown does
An artifact with no N card IS fossil wiki — fossil's default, and what anything writing through the zit CLI produces. fossilInline resolved this dialect's own [target] links, but @mentions, [[wiki]] refs and bare hash refs are dialect-independent and were reaching only markdown bodies, so 62 of quarry's own 90 forum posts lost every mention in them. The two passes run after sanitizing, as they do in Render: resolveRefs mints the identity mark and the policy would strip it. It parses HTML and skips a/code/pre, so the anchors fossilInline just built and every block are left alone — both asserted.check-in: b00045e163 user: dmestas tags: trunk | |
|
2026-08-26
| ||
| 23:51 | Landed layer 6: the heading gone, the strata proposal in RFC 0006. check-in: 2a95e40671 user: ai-local tags: trunk | |
| 23:51 | Landed phase 5: every row of the RFC's table, proved the way a person would do it. check-in: 763b776f6f user: ai-local tags: trunk | |
| 23:51 | Landed phase 4: the rule has a page, and the page says what it costs. check-in: 8f527acdb5 user: ai-local tags: trunk | |
| 23:51 | Landed phase 3: the merge is the fact, the reply is the claim, accept asks MayJoin. check-in: 2d30e99800 user: ai-local tags: trunk | |
| 23:51 | Landed phase 2: identity becomes a rule, the child enforces it. check-in: 8c6e2b5b59 user: ai-local tags: trunk | |
| 23:51 | Landed phase 1: the policy table and the decision function. check-in: 4742a00a65 user: ai-local tags: trunk | |
| 23:31 |
Landings list: drop the h1; RFC 0006: how a strata is shown
The tab already says landings and the page's first line is the state chips; the heading said the same word a third time. RFC 0006 gains the rendering proposal it deferred, written after this forge's first real strata listed as five independent open landings. Grounded in what GitHub (native stacks, July 2026), Gerrit (relation chain, submit whole topic) and Graphite do: a position on every member, a map on the record, one action at the landable end. quarry groups in the list because its list is a queue and a queue that shows five rows for one decision counts wrong. closed check-in: 3693d77992 user: dmestas tags: protect-6 | |
| 23:24 |
RFC 0007 phase 5: the matrix, driven through the forge
Every row of the RFC's table, the way a person or a client would do it: the forge's accept through the API as owner and non-owner; a clone's accept pushed through the sync gate, refused whole with the branch named, then admitted for the owner with no reply posted; a stock fossil client's merge refused; branching off trunk and pushing to an unprotected branch admitted; a forged accepted reply recorded as a claim. A branch continued past its merge stays accepted. Compatibility and disposability as the RFC states them: a clone's artifacts are identical with the row present or absent, and the record renders identically before and after the row, and across the section-11 wipe. The disabled control with its reason is proved in the web package, where a viewer has an identity — and proving it found the landing page asking MayLand about nobody: the viewer now travels with the read. Three rows are proved elsewhere and named in the file so the matrix reads complete. closed check-in: 2a68c15ebf user: dmestas tags: protect-5 | |
| 23:16 |
RFC 0007 phase 4: the settings surface, under a
Protected branches beside capabilities on the repository settings page: a branch or glob per row, owners as logins, every matching rule applies, blank the owners to remove one. The form rebuilds the stored set, so a renamed row is a delete and a write. The cost is stated where the rule is set, because both things it changes surprise someone who knows fossil: on this forge i means less than it means in fossil, and a stock client can be refused a push its capability string says it may make. Policy of this forge, outside the repository; a clone is byte-identical to an unprotected one. The section is not drawn on a forge with no policy store. closed check-in: 0d3bcdd784 user: dmestas tags: protect-4 | |
| 23:13 |
RFC 0007 phase 3: the merge is the fact; accept asks MayJoin
The fold reads accepted from the DAG, for every landing: the earliest target check-in after opening whose non-primary parent is on the proposed branch. A reply asserting acceptance with nothing merged leaves the record open and is recorded as a claim; the page says so, the timeline files it as conversation. Rejection has no DAG fact and stays the reply's to say. Re-derived after every branch recompute — which now also invalidates the count memo, since it can settle a landing — so a merge pushed from a clone with no reply settles. landing.accept refuses a non-owner on a protected target before writing, naming the owners. landing.get reports MayLand beside CanAccept so the page shows a disabled control with the reason. Fourteen existing tests settled records by reply alone, or asserted a by-hand merge does not count; each now carries the merge it announces or asserts the RFC's reading. Giving the web fixture real check-ins exposed checkin.diff dereferencing a nil store; it now refuses, after the lookups so not-found still wins. closed check-in: 4832508615 user: dmestas tags: protect-3 | |
| 22:59 |
RFC 0007 phase 2: the gate turns identity into a rule
The verified login — never the U card — becomes the per-request X-Zit-Protect the --fronted child enforces: the globs this pusher does not own, comma-separated as zit's glob lists are. Sent on EVERY request, empty when nothing is refused. zit#151 reads an absent header as no rule, so absence is exactly what a forgotten code path would produce; here it cannot happen, and a test proves it against a fake child. The proxy builds a fresh request, so a client's header with that name never reaches the child. A rule source that cannot answer fails the push closed. End to end with a real zit: a non-owner's push to trunk is refused with the branch named; the owner's identical push lands. closed check-in: daa9fc84b5 user: dmestas tags: protect-2 | |
| 22:55 |
protect: the purge drops the rules, and the test asks the purge
The first test called DeleteRepo, which drops the repo row and leaves per-repository state to PurgeRepo — the cascade the rule was added to. Committed red because the run gating it used a regex that matched no test; caught by the suite. closed check-in: f30535d269 user: dmestas tags: protect-1 | |
| 22:54 |
RFC 0007 phase 1: branch_protection and MayJoin
Forge policy beside repo_caps: a repository names protected branches (name or glob) and their owners. No trace in the repository; edited under 'a'; empty by default so an imported store behaves as it did. MayJoin answers may-this-account-change-this-branch's-membership. Every matching rule applies, so a glob and a narrower name on one branch can only narrow. Refused lists the globs an account does not own — the per-request rule the sync gate will hand its child. A store that cannot answer says no: silence is not consent. check-in: 70d043b2e7 user: dmestas tags: protect-1 | |
| 22:50 | Accepted. The fold reads a fact; the gate owns the guarantee. check-in: c74cc272cb user: dmestas tags: trunk | |
| 21:17 |
RFC 0007: fold reads the merge unconditionally; the gate owns fail-closed
Three amendments from review, now that zit#151 has shipped. The fold's DAG-based reading applied only to protected targets, so adding or deleting a protection row re-judged every landing on that branch — the thing Disposability forbids. It is now unconditional: a DAG fact is the same fact under any policy. The merge is identified structurally (non-primary parent on the proposed branch, bounded by the open date), not by tip ancestry, which breaks for a branch merged and then continued. zit#151 shipped with an absent header meaning no rule. Correct for a bare server; behind a front it means a gate bug silently disables protection. The RFC now records that and places three obligations on the gate: always set the header, strip the inbound one, prove both. Test matrix gains the missing rows; What-this-is-not records the supersede interaction; build order marks phase 0 done. closed check-in: 2eff0c5d61 user: dmestas tags: rfc-0007-review | |
| 16:33 |
comments: render fossil links in the timeline, and three routes that were dropped
The timeline printed check-in comments as escaped text, so fossil's own bracket links showed as markup. commentHTML already existed and was wired to the check-in page only; the timeline now uses it for the lead line and each paragraph. Flat rows keep plain titles — theirs are drawn inside a link already, and an anchor cannot nest. Surveying every link form fossil writes turned up three quarry HAS a page for and dropped anyway: /info, /artifact and /whatis (21 citations across the corpus) reach the same page a bare [hash] does, and /tktview reaches the ticket. A hex tail is required, so /info/2020-06-02T17:38z keeps its documented fate. An unlabelled route printed its own path as the link text — markup leaking into prose, the one thing this renderer must not do. Those now read as prose. /forumpost still aims at the forum index on purpose: measured against the fossil clone, 303 of 304 cited forum hashes are not present locally, so a deep link would 404. check-in: 51930149be user: dmestas tags: trunk | |
| 15:46 |
landings: one write path for remarks again (zit#149)
zit landing comment grew --reply-to, so a parented remark no longer has to detour through forum reply. Both cases go through LandingSay, which means the landing-specific error handling below — conflict and refusal decoding — now covers every remark rather than half of them. The same-thread check stays. zit refuses an out-of-thread target too, but here it is a capability boundary: quarry does not delegate its permission model to an upstream refusal. Artifact cards verified unchanged: G=root, I=parent, identical to what the forum-reply path produced. check-in: 3886890ee7 user: dmestas tags: trunk | |
| 13:49 | Accepted. The pattern hid two pages; the panel showed which one was slow once it stopped showing the pattern. check-in: 7ec507a60e user: dmestas tags: trunk | |
| 13:49 |
Dashboard: memoise the per-kind census; stop filing assets as renders
Two things the ops history surfaced. GET /{$} serves two pages: the fleet for anonymous readers and the dashboard for signed-in ones. The census fix landed earlier addressed the fleet listing, so the spike a signed-in reader sees was never touched — the route pattern hid two different pages, which is the thing the drilldown exists to expose. Measured cold across this fleet, EventKinds is 610ms of that page: a full covering-index scan of every artifact, per repository, for numbers that only move when a fold does. Memoised beside the other two, invalidated with them. TicketCounts measured 110ms and was left alone. Asset requests are file serving, not renders. The browser asks for a sourcemap nobody ships, so /assets/datastar.js.map fell to the catch-all and filed a 404 against route '/' several times per page view. closed check-in: 9bc994bada user: dmestas tags: ops-noise | |
| 13:45 | land protected-branches check-in: caaa75e5e6 user: dmestas tags: trunk | |
| 13:41 |
RFC 0007: protected branches — ownership enforced where identity is a fact
A check-in joins a protected branch only by an owner's hand; accepting a landing on it follows from that rather than being a second rule. Policy lives in OLTP beside caps, is evaluated at the xfer gate and the accept op, and writes nothing to the repository — fossil compatibility is a normative section, not a hope. The one zit change is drafted in Appendix A. check-in: d4cf4acf26 user: dmestas tags: protected-branches | |
| 13:37 | Accepted. Clicking through found what testing the parts did not. check-in: 111491a3a9 user: dmestas tags: trunk | |
| 13:37 |
ops: the path actually requested, and two broken links
Recent renders showed the route pattern, so a dozen rows all read GET /r/{repo}/timeline and none could say which repository was slow. The URL is now recorded and shown, with the route beside it as the link to its drilldown. Two bugs found by clicking through rather than by testing the parts: queryesc double-encoded every URL it touched, because html/template already escapes a value in query context. The routes panel linked to its own drilldown with %252F for a slash, so every link 500'd. Six call sites, all wrong; the function is gone. The days control I added with the trend forwarded an empty parameter, and a numeric field with a default takes it when the key is MISSING and refuses when it is present and empty. Routes panel gains a filter and a sort; renders gain a deeper-history page reading the store rather than the ring. closed check-in: 2d033f106e user: dmestas tags: ops-paths | |
| 13:35 |
landings: mark the two-verb remark path as blocked on zit#149
Filed upstream: landing comment sets G and I to the record root, so it cannot aim a reply. The forum-reply route stays until that lands; quarry ticket 6f4c8579f0b5 tracks collapsing the fork. check-in: b459791bbd user: dmestas tags: trunk | |
| 13:28 |
footer: shed the timing breakdown before it overlaps the links
At phone width the brand and version already retire, but the timing string alone still outran the viewport — and being flex:none it printed over the footer links rather than giving way. The per-store breakdown now retires at the same step the brand does; the total, which is what the footer is for, survives. The collapse test pinned the whole media rule, so adding a part to a step read as a regression. It now matches the breakpoint and the parts within it, and asserts the total is never itself hidden. check-in: d7ff96b534 user: dmestas tags: trunk | |
| 13:13 |
landings: keep the reply badge and the state mark in one grid cell
t-row is a three-column grid. The reply badge made the state mark a fourth child, which wrapped to an implicit row under the glyph — the accepted pill dropped onto a line of its own. Both now sit in a t-tail cell, so neither can displace the other. check-in: f876bfc649 user: dmestas tags: trunk | |
| 13:09 |
landings: forum niceties — reply count, threading, per-post reply
The landing list carries a reply badge, the same .t-conv/#i-comment the ticket list uses; the count excludes state transitions, which are events rather than things anyone said. The discussion renders as the thread it is: the forum's own forumPostRow, participant spectrum, hue edge, indentation and 'replying to' attribution. The threading walk moved out of the forum handler into threadShape so the two pages cannot drift. A remark aimed at a post goes out via 'zit forum reply' — a landing thread is a forum thread of the same artifact kind, and that is the one verb that can point the I-card at something other than the opener. The target is checked against the record's own thread first. Merges the superseded/CI work that landed on trunk meanwhile. check-in: 5815a2a7d8 user: dmestas tags: trunk | |
| 12:58 | Accepted. Ordering by the DAG, one current landing per branch, and a verdict that was already an artifact. check-in: db0ed60824 user: dmestas tags: trunk | |
| 12:56 |
A landing's CI verdict: the branch tip's tag, nothing new minted
The ticket asked to decide the shape — forum reply or tag — before wiring the display. Neither is needed: a verdict is already a tag on a check-in, quarry already folds and reads them, and a landing proposes a branch whose tip is a check-in. Better on its own terms too. The verdict stays attached to what was actually tested rather than to a discussion record, keeps the zit#127 invariant with no new vocabulary, and a retargeted or reopened landing inherits the right answer without anyone copying it across. Read at render, never stored, so it cannot go stale against the branch it describes. No run leaves it empty, which reads as 'nothing has run' rather than as bad news. closed check-in: 1305bb9374 user: dmestas tags: landings-batch | |
| 12:53 |
One open landing per branch: refuse the second, and agree which wins
zit#127 left this as 'probably yes, refused by name on the second' and neither side enforced it, so two threads naming one branch produced two open landings with nothing saying which accept should honour. Both halves, because refusing is not enough: two peers can each open one in separate clones and both are valid artifacts when they meet. landing.open refuses by name; the fold picks the newest root as current and marks the rest, recomputed each fold so a record revives if the one that displaced it is settled. Settled landings never take part — a later proposal for the same branch is a new proposal, not a correction of a finished one. Nothing is deleted; a record somebody argued in is not noise. check-in: c2930a1351 user: dmestas tags: landings-batch | |
| 12:48 |
A landing's commits in ancestry order, not date order
The page listed the branch through the timeline, which orders by (date DESC, hash DESC). Two check-ins in the same second tie on date and then order by hash, which has nothing to do with which is the parent. Commits land back-to-back here routinely. zit hit this in landing show and its fix is the one to inherit: the verb owns no ancestry ordering of its own. A recursive walk from the branch tip, ordered by longest path back, stopping where the branch diverges from its target. The test commits three times in one second with hashes whose order is the reverse of ancestry — the old sort gets it exactly backwards and looks plausible doing it. check-in: 21fde3240d user: dmestas tags: landings-batch | |
| 12:44 | Accepted. A revision cannot go stale; a rendered row can. check-in: 105cfb9c18 user: dmestas tags: trunk | |