Commit graph

2774 commits

Author SHA1 Message Date
Fini eb02fd2fe0 chore(editor): re-render stale preview artifacts
Brings every preview back to what the current binary produces for the
current document: 80 frame and overview PNGs and 18 card JPEGs. Most
follow the tracking re-alignment and the board re-wrap, but a share of
them had drifted purely because the renderer was rebuilt — the audit
does not distinguish, and does not need to, since the invariant is
simply that a saved image equals a fresh render byte for byte.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 21:17:24 +08:00
Fini ad1ed80a8d test(editor): audit preview artifacts by re-rendering them
Preview PNGs and card JPEGs go stale two ways. Editing a document and
forgetting to refresh is the obvious one. The other leaves no trace at
all: the renderer changes, the document does not, and every previously
saved image is silently wrong while git is clean, the tests are green
and the gate passes. That is what happened to three minimal-keynote
frames whose content matched HEAD exactly but whose pixels no longer
did, because the desktop binary had been rebuilt underneath them.

Nothing but re-rendering and comparing bytes can see the second case, so
that is the only criterion here. It rests on rendering being
deterministic — two runs of one document must be byte-identical, which
`--selftest` checks; if that ever stops holding, this tool's verdicts
are void and the renderer is the thing to fix.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 21:17:01 +08:00
Fini 4064f9c6dd style(editor): re-align template tracking to the ratio cap
Fifteen templates carried negative tracking past the cap the previous
rule failed to enforce — 51 text nodes in all, and nothing else in the
documents moves. minimal-keynote's four are hand-written literals in its
generator, so that one is fixed at the source rather than only in its
output; the rest come out of the shared `text()` clamp.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 21:16:46 +08:00
Fini 98f70579bf fix(ai): compare the CJK tracking cap as a ratio, not a rounded number
The negative-tracking cap was stated as `round(fontSize * -0.02)`, and
rounding it was wrong in both directions: at 76px it let -2 through when
the real cap is 1.52, and at 72px it failed a legitimate -1.4 because the
cap rounded down to 1. A rule that both over- and under-fires is not a
threshold worth tuning, so the comparison is now against the ratio itself
with fractional values allowed.

Two narrowing decisions come with it. The cap applies only to runs that
actually contain Han characters — Latin and numeric display (page
numbers, stat values) legitimately sets -0.03 to -0.05em, and 160 nodes
across the library were being flagged for it. And the 64px boundary is
gone, because below 48px the ratio is already self-limiting.

The clamp sits in `text()`, the one entry every generator goes through:
a single tracking literal is routinely shared by several sizes, so
fixing call sites one at a time is guaranteed to miss some. `trackcheck`
makes the rule enforceable outside the two generators that imported the
kit, which is how the drift got in unnoticed.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 21:16:35 +08:00
Fini 3b9fefde26 fix(ai): compact sub-agent skills before spending the budget
Sub-agent prompts budgeted first and compacted second, so the knapsack
paid for skills the compaction was about to delete — and when the bill
came due it evicted the deck material instead. A weak model then designed
a deck with no deck guidance at all, silently, because the corpus on disk
was still correct. The order is now compaction first, and the wrapper
that made the wrong order one call away is gone rather than kept for
symmetry.

`deck-contract` joins the sub-agent allow-set for the same reason its two
neighbours are there, and the deck budget arm asks the single classifier
instead of comparing widths inline: the hand-rolled bound had no aspect
gate, so a tall 1920-wide page claimed the deck budget and spent it on
slide teaching it could not use.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 19:57:24 +08:00
Fini 53fc40f305 feat(agent): judge a deck board by its geometry as well as the prompt
Deck centring fired only when the prompt said "deck", so the agentic
loop — which has no prompt to read — left every board top-stacked with a
blank lower half. The geometric half of the judgement is now unioned in
at the point of use, covering the paths that have no request to consult.

Centring is an intent-tier move, though: an asymmetric board can be
exactly what the author composed, and the explicit-`justifyContent`
guard does not catch an author who simply placed content and never set a
distribution. So the geometric half is gated on the roots being this
run's own output, which the fresh and append paths can prove and the
whole-document finalize cannot. The prompt half stays ungated — a user
who asked for a deck stated the intent themselves.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 19:57:11 +08:00
Fini 4f124b0b1d feat(agent): report deck row overflow instead of clipping it
The horizontal-overflow floor sets `clipContent` on a row whose children
do not fit. On a scrolling screen that is recoverable — the content can
still be scrolled back into view. On a projector board it is content the
audience never learns exists, and the honest fixes (shorten it, re-type
it, split the page) are all decisions this pass cannot make.

So a board now reports the overflow and leaves the row visibly too wide.
The report travels as an echo rather than a repair record, because
nothing was repaired and a record would claim credit for a fix that
never happened: the finalize path notes it onto the user-visible
summary, and a sub-task, which has no summary to write to, logs it.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 19:56:54 +08:00
Fini f10b660099 feat(agent): hand geometry validation the surface it is validating
Every threshold the geometry collectors use is tuned for a screen, and
the deck collectors that need their own floors land next. Giving them
the form through the single classifier now means each arrives without
measuring the root's width for itself.

No collector branches on the form yet. The debug line is there because
until one does, "the deck floors did not fire" and "the board was never
classified as a board" are the same symptom from the outside.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 19:56:38 +08:00
Fini 4ae03919f9 refactor(agent): move the surface classifier below the orchestrator
The design lint detectors need the same answer the repair passes do —
what kind of surface is this root — and the detectors sit in a crate
below the orchestrator, so the judge has to live there too. Otherwise
each detector would re-derive the form from a width comparison of its
own, which is precisely the drift the single classifier exists to stop.

Nothing changes behaviourally: the orchestrator re-exports the types so
every existing import path still resolves, `detect_all` classifies its
own root, no detector branches on the form yet, and the plan layer's
mobile-width constant now aliases the classifier's band instead of
carrying a second copy of the number.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 19:56:12 +08:00
Fini eeb6819c2c feat(ai): teach the corpus how a projector board differs from a page
Deck output kept inheriting page reflexes — screen-sized type, page
margins, a row clipped instead of split — because nothing in the corpus
stated the laws a 16:9 board is read under. `deck-contract.md` states
them once (back-row type floors, density budgets, narrative arc, the
slop bans), and six style guides give the new deck templates the same
authored voice when a request names one.

The generation budget moves to 13200 so the deck material is additive
rather than evicting the knowledge skills it depends on, and a standing
test pins the report to the knapsack's own accounting — a
budget-exhausted drop now provably means the skill did not fit.

The CJK kerning rule lands on minimal-keynote, whose display tracking
was tight enough to collide glyph side bearings at large sizes.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 19:55:26 +08:00
Fini c37a109e3c refactor(editor): wrap multi-board templates onto rows
Every multi-board template laid its boards out in one long horizontal
strip, so opening a seven-board carousel dropped the user onto a canvas
they had to pan sideways to survey. Three per row matches the deck kit,
and the extra row gap is deliberate: the canvas draws frame names at a
fixed screen-space offset, so at fit-to-screen zoom a plain column gap
lets the second row's labels sit on top of the boards above them.

knowledge-carousel additionally takes hard line breaks in two body
paragraphs. Greedy wrapping put a comma at the start of a line and left
a lone full stop on the last one, which are both CJK line-break faults;
authored breaks remove the engine's freedom to reintroduce them.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 19:55:00 +08:00
Fini 267a08f7aa feat(editor): register the six deck templates in the picker
Wires the new decks into the catalog, the preview grid and all fifteen
locales, so they are reachable rather than merely present on disk.

The preview cards ship as JPEGs like the rest, which grows the staged
scene-template payload; both figures in the asset notes are refreshed
so the wasm budget stays auditable against a real number.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 19:54:23 +08:00
Fini 728a32e7f6 feat(editor): add six 16:9 deck templates
The scene-template library carried one deck against a dozen carousel and
tutorial boards, so anyone starting a presentation started from a blank
board. These six cover the archetypes that actually get presented:
consulting strategy, data review, editorial keynote, workshop notes,
case dossier and financial ledger.

Each is seven or eight boards built from a shared generator kit, so the
grid, type scale and board wrap stay one decision rather than six.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 19:53:37 +08:00
Fini 3a8c154739 fix(renderer): keep transparent decoration layers out of the way of clicks
Two carousel templates stack a full-bleed transparent shell (washi tape,
punch holes, film sprockets) in front of their content. Every click over
the board landed on that shell instead of the text behind it.

The engine half is the jian bump: a container that paints nothing no
longer claims its own body in the hit-test. The template half marks
those shells `locked`, so the shell is unselectable as a layer while the
marks inside it stay individually selectable — dragging a piece of tape
no longer drags the whole layer, and a marquee no longer nets the board.

Both templates are regenerated output, so they also carry the row wrap
their generators gained in the same pass.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 19:53:09 +08:00
Kayshen-X e6013d71da fix(collab): rotate production trust roots safely 2026-08-09 17:17:08 +08:00
Kayshen-X 7a24f227aa fix(web): gate unix-only signal + test helpers off the windows build
Windows clippy under -D warnings flagged two items as dead code: the
SIGNAL_RECEIVED atomic (read only by the cfg(unix) signal handler and
its watcher) and with_test_binary (called only from the cfg(unix) exit
tests, whose stand-ins are /bin/sh scripts). Both now carry the same
gate as their sole users, so they stay live-code on every platform.
The whole native test suite (9796 tests) is green on windows this
round; this clears the last clippy warning.
2026-08-09 13:51:50 +08:00
Kayshen-X 98d0c15e1c chore(web): forward the dev op-auth ticket feature to the serve-web daemon 2026-08-09 12:52:07 +08:00
Kayshen-X c1b7b961c2 test(cli): windows-safe temp names in the modify smoke, enumerate failures
The alias test keyed its temp file on the thread name, which for a
test is its full "mod::tests::name" path — and Windows rejects ':' in
file names, so the write panicked before the assertion ran. The name
is sanitized to alphanumerics now. The windows nextest run also gains
--no-fail-fast: cancelling on the first failure had been surfacing the
latent platform-only failures one per CI round.
2026-08-09 12:38:23 +08:00
Kayshen-X 203d47f3d8 feat(web): theme becomes a device preference
The theme now lives under its own unpartitioned storage key — the
account/device boundary sits in storage itself rather than in which
fields of a shared blob a reader may trust. Switching accounts keeps
the device's theme, the account-scoped reset leaves it alone, and an
existing account's stored choice migrates to the device key once (the
account blob keeps a write-only copy so an older bundle or a second
tab on the previous build does not snap back to the default). The save
runs outside both persistence-fingerprint gates: an account blob the
tab refuses to write must not take the device preference down with it,
and a refused write stays retryable instead of silently losing the
choice for the session.
2026-08-09 12:36:02 +08:00
Kayshen-X d023dbf1de feat(web): fetch scene templates and the icon catalog at runtime
The 58 template documents and the core iconify catalog follow the
previews out of the wasm binary behind the same asset seam: templates
resolve by id on both platforms but carry bytes only natively, so the
wasm boot check validates routes instead of rejecting the catalogue,
and a click on an unfetched card requests the asset and instantiates
on the install edge (failure raises the retryable toast). The icon
picker prefetches while open, distinguishes "still loading" from "no
match", and keys its search memo on catalog readiness so the empty
pre-fetch result cannot be memoized for the session. The split is a
feature (runtime-icon-catalog) enabled only by op-host-web — the
web-sdk viewer has no daemon to fetch from and keeps the embed, which
a first cut silently broke. Bundle: 5.10 → 4.93 MiB gzip; the sdk
bundle (5.20 MiB measured) gets a calibrated 6 MiB tripwire.
2026-08-09 12:26:39 +08:00
Kayshen-X b01551b7fe test(canvas): budget isolated seam pixels in the pan-cache diff
Windows' scalar rounding lands one glyph/tile edge a hair differently
(exactly 1 px on the CI runner), which the zero-diff rule read as
misregistration. Real misregistration swaps whole fills — hundreds of
contiguous pixels — so a 4-pixel isolated budget keeps the guarantee
while absorbing the platform rounding the comment already describes.
2026-08-09 11:54:06 +08:00
Kayshen-X ba2f55d4b5 ci: follow the submodule pins in the nix flake inputs
The flake pinned jian/casement as fixed github revs while the repo
advances the same code as submodules, so every submodule bump that
forgot the flake broke the whole workflow — most recently op-pen-loader
failing to compile against a jian without the widget-style fields. The
workflow now derives --override-input values from the checked-out
submodule HEADs (with --no-write-lock-file), so it follows the gitlinks
by construction, and the flake.nix default revs are refreshed to the
current pins for local use.
2026-08-09 11:30:26 +08:00
Kayshen-X c4a2d5e0d7 fix(web): browser smoke reads the mount marker from the console stream
The editor now holds a live SSE stream, and an in-flight network
request pauses headless Chrome's virtual clock — so --dump-dom never
flushes, the DOM grep never matches, and the smoke either timed out
(CI, 45s) or hung on a TERM-immune Chrome behind a bare wait (observed
12 hours locally). The mount marker is now mirrored to the console,
which reaches the stderr log incrementally, and the poll loop treats
that line as readiness, synthesizing the DOM marker the assertions
expect; the timeout path uses SIGKILL. Verified end to end locally:
both the pure CanvasKit page and the daemon host page mount.
2026-08-09 11:27:59 +08:00
Kayshen-X f58c5bf9b3 test(desktop): unflag the redaction fixture and de-unixify relay path tests
The boundary gate's credential scanner greps source literally, so the
redaction test's sk-ant fixture is now assembled with concat! — the
runtime string is unchanged, the source no longer looks like a leaked
key. The relay token-policy test built /policy.json-style paths that
count as relative on Windows, so the config constructor's absolute-path
check failed before the assertion ran; the paths now carry a drive
prefix there.
2026-08-09 08:59:43 +08:00
Fini 6ca4e6e504 test(editor): count the two web prompt previews in the route audit
The distinct-route audit landed upstream counting fifty-seven
previews; the two web-landing prompt entries make it fifty-nine.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:40:28 +08:00
Fini 7970c0c108 chore(editor): keep the template QA gates beside the generators
cjkcheck (CJK line-break simulation), cjkreal (resolved-height cross
check that filters simulator false positives) and gate.sh (audit +
both, per-file verdicts) move from session staging into the generator
tree so the next template batch starts with the gates that vetted
this one. stubcheck is deliberately not kept — the audit's diagnostics
already cover empty decorated stubs via the same predicate.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:24:44 +08:00
Fini 6fd0b343a1 fix(editor): tighten template copy that wrapped into orphan lines
A CJK line-break gate over all fifty-eight templates caught eleven
sets whose body copy wrapped into three-character orphan tails or
punctuation-led lines. Each line is shortened to fit its measured
column (paired sides stay symmetric where the layout demands it),
affected root heights re-measured, previews re-baked. The one
remaining gate report is a simulator false positive disproven by the
rendered frame.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:24:44 +08:00
Fini 3a38aa9638 fix(web): satisfy canvaskit clippy on the live-sync modules
Test-only imports and a four-slash comment slipped in ahead of the
canvaskit-feature clippy sweep; the all-targets -D warnings gate
compiles clean again.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:24:44 +08:00
Fini 7f77e3853d test(agent): cover tier gating through the cleanup driver
Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:24:44 +08:00
Fini c998d551eb feat(editor): grow the template library to fifty-eight across six scenes
Every non-deck, non-web scene now holds ten templates: cards (spec
themes as living samples), infographics, tutorials, comparisons and
carousels (nine spec themes with per-set master-board rules), plus
two web landings whose 'generate from this' pins their measured
style guides — a basis the document swap used to drop before the
turn ever saw it. All fifty-eight ship generator-reproduced (sha256-
matched), audit-clean, with baked preview cards, and the scene tab
row pins Slides second-from-last and Web last. Two prompt-center
entries carry the web pair with re-rendered previews and honest
render provenance.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:24:43 +08:00
Fini ab2b4757af feat(ai): nine original style guides join the corpus
Two web-landing voices measured from shipped documents, a butter-
paper serif editorial, and four social-card themes (mineral cave
dark, letterpress vermilion, arcade neon night, highlighter
notebook) — palettes derived from real anchors via the sampling
protocol, prose written fresh, platform-tagged for the card shelf.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:24:43 +08:00
Fini 06c4fa7f21 feat(agent): thread repair records and notes through loop and web streams
The agent-loop ack and the web thinking stream carry the itemized
ledger the desktop path already surfaces, so every route can expand
the polish step into its records.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:24:43 +08:00
Fini 95576d7af3 fix(renderer): hit only the active tabs panel and drop unpainted widget strokes
jian-scene now exposes visible_children() as the single judgment both
paint and hit-test share, so canvas clicks land on the tabs panel the
user sees instead of a hidden sibling occupying the same grid cell
(vendor/jian bump carries the walk change, rebased onto the thumb
registry work). A stroke that declares thickness but resolves no
paint no longer masquerades as opaque black on widget tracks and
borders — three unpaintable shapes drop it and the role defaults take
over, while $ref-driven and ordinary shape strokes keep their
historical behaviour. Zero-thickness tab bar strokes stop painting
skia hairlines.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:24:43 +08:00
Fini 2a5c230707 feat(editor): asset center round — responsive gallery, palette bands, one control language
Columns derive from a card-width ceiling instead of three hardcoded
breakpoints (a 5120px window packs eleven columns, not four plus
half a screen of black), each card carries a palette band memoized
from its document's declared variables in declaration order, long
summaries appear on hover, and the panel's chrome converges on one
segmented control, one chip surface and a two-step pointer ladder
from a shared metrics module. Style import gains a file-picking path
on both hosts that feeds the same pipeline as pasting — the second,
divergent import pipeline is deleted.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:24:43 +08:00
Fini 597cbbeb7d feat(editor): scale the prompt center with the viewport
The 720x520 box becomes an 88%-of-viewport gallery sharing the asset
center's breakpoint functions — columns follow width, cards keep
their aspect, and the shell clamps instead of overflowing on small
windows.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:22:03 +08:00
Fini 9348f856d1 feat(editor): name exports after the document and selected node
A single selected node exports as '<document>-<node>.<ext>' on both
hosts from one shared sanitized stem (dangerous characters swapped,
per-character CJK truncation, no stutter when names match), the deck
PDF route accepts a board subset that filters in deck order rather
than request order, and an empty subset means nothing-to-export
instead of silently shipping the whole deck.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:22:02 +08:00
Fini 9534802ce4 feat(editor): slides rail present and export actions
The rail's footer becomes a two-control bar: Present enters the
existing preview, Export PDF opens an upward menu whose second row
exports exactly the selected boards — counted from the same
chips-and-selection intersection the exporter consumes, greyed at
zero so the upward menu never changes height under the pointer.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:22:02 +08:00
Fini b5d73b907a feat(editor): chat panel round — chips, thinking, caret, hover card
One pass over the floating chat panel: the style receipt and
selection chip share a single 28px row (down from two 52px slabs)
with one rect source for paint and hits; the minimized bar borrows
the expanded panel's width instead of a hardcoded 400; a three-state
thinking toggle joins the footer (live even mid-stream — that is
when you want the next turn to stop thinking) and the orphaned
controls row it replaces is deleted; arrow keys walk the input caret
across visual rows with scrolling that follows it, wheel over the
input scrolls the input, and IME composition is never intercepted;
hovering the style chip raises an info card (source badge, palette,
fonts, first summary line) fed by a memoized corpus lookup; and
zero-width footer slots stop answering hits on their origin column.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:22:02 +08:00
Fini e86f0f4736 feat(editor): tighten the settings panel to a shared row scale
The hero block shrank to a section heading, provider entries traded
bordered cards for hairline rows, and the scattered paddings, row
heights and section gaps collapsed into one named metrics module the
whole family reads. Paint and hit-test walk the same row ladder, so
the provider rows' four-pixel click offset is gone, and the panel
shell itself comes down to 960x760.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:22:02 +08:00
Fini 4075985628 feat(agent): route social-card prompts to a first-class card type
'Make me a xiaohongshu card set' used to hit the component trigger
and come out 400px wide. Card is now a design type with a 1080x1440
preset (checked ahead of component, vetoed by 组件), its own style
shelf behind a hard platform filter, prompt-inferred card tags, an
aspect lock that still honors explicitly requested dimensions, and a
fallback plan that labels every card as its own screen.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:22:02 +08:00
Fini 4abd1926d7 feat(agent): tier repair passes and scope them by provenance
Splits every cleanup pass into contract (an unrepaired screenshot
would show something no author intends) and intent (taste calls the
author already made). Template-provenance documents skip the intent
tier outright — audited template styling is authored truth — and
appended turns run intent only on the inserted roots while contract
checks sweep the whole target root, so trailing sections are no
longer a blind spot when insertion lands empty. Skips are recorded
as ledger notes, never silent.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:22:02 +08:00
Fini fc19140823 fix(agent): repair passes must prove what they claim to fix
A field audit of real generations convicted a family of passes that
asserted redundancy without proving it: section rhythm zeroed because
a machine gap 'stood in' for padding it could not supply, intentional
dark bands stripped as repaints of a ground the root never painted,
headings bound to a border token by hex distance, near-white borders
surviving dark designs, starved rigid rows crushed to 2px, overlays
buried under opaque siblings, watermark numerals flagged as unreadable
collisions, and the whole CJK nav chain dead because label
normalization kept ASCII only. Each rule now states a provable
contract — gap must absorb the inset, the ground must match, slots
reject impossible families, rows respect rigid content, ink decides
collisions, containment decides board ownership — and scrolling-page
sections keep their authored rhythm outright.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:22:02 +08:00
Fini f0e3ef48c0 feat(agent): itemize every quality repair into an expandable ledger
'41 auto-repair(s) applied' told the user nothing about what changed.
A counting-sink decorator now captures each accepted edit as a record
(pass group, node, field-level before/after — removed attributes read
as '(unset)', not a dash) at the one point every command crosses, so
counts derive from records and cannot drift. The polish step expands
into one row per record on desktop and as sub-rows on the text
streams, capped inline with the remainder in the INFO log, and scope
notes (skipped tiers) always precede the repairs that did run.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:22:02 +08:00
Fini 6c05d0a7a8 fix(mcp): stop later batches consuming named screen shells
The replaceable-empty-root snapshot reset per program, so screens a
previous batch inserted (still empty, but named) read as starters and
were swallowed by the next insert. Only an unnamed frame — or the
factory starter's literal name — is replaceable now.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:22:02 +08:00
Fini 4b47e8a103 fix(agent): gate root-seed inheritance on real continuation intent
Preserving original-case screen names broke the lowercase existing-
screen guard, quantifier stripping killed whole target lists over one
short latin name, and root-seed inheritance fired on any canvas that
happened to hold a same-class screen — silently overwriting authored
widths and fills. Continuation is now an explicit signal derived from
the same promised-screen parser the orchestrator uses, inheritance
never overrides an authored fill, and the guards are case-robust.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:22:02 +08:00
Fini cf7df18ac1 fix(ai): route smoke reasoning controls through the live wire builder
The harness re-derived its reasoning wire shape from a stale
capability check, so kimi-k3 benchmarks ran without the control the
product sends and force-disable posted the exact body field that
model rejects. apply_reasoning_wire_control is now pub and the smoke
clients call the same mutation the live turn does, asserted per model
id against the production body.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:22:02 +08:00
Fini 77965f85f9 fix(services): parse the two-column antigravity model catalog
agy 1.1.11 switched 'agy models' from one slug per line to
'id<TAB>display name'; the parser kept whole lines so every --model
value carried the display name and the CLI rejected it. Split rows on
the separator (slug-shaped left cells only), refuse to treat a
--model rejection block as a catalog, drop ids carrying column
separators as a format-change tripwire, and log a version/format
breadcrumb only when the pair changes.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:22:02 +08:00
Fini d00c763f6e fix(services): carry cli output into failures as bounded redacted tails
A failing CLI turn surfaced only its exit code: the stderr drain task
raced the tail read (a ~40% loss under concurrent subtasks), the
fallback branch discarded whatever the tail held, and friendly
classification swallowed the evidence when it did match. Failures now
join the drain within a grace window, quote a head-and-tail excerpt
with secrets redacted before truncation, keep the classified verdict
and the excerpt together, and the ACP connect path stops discarding
its child's stderr line by line. Auth keyword matching gains word
boundaries so 'authored' no longer reads as an auth failure.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:22:02 +08:00
Fini 310f40083d fix(collab): make the hsm relay locator unix-only
The signer daemon's whole security surface (SO_PEERCRED caller
authentication, socket-file mode/owner checks, O_NOFOLLOW opens,
flock single-instance) is unix semantics; config.rs paths are POSIX
too, so windows targets got fifty-five compile errors and a runtime
test failure. Gate the crate at the root with a stub main instead of
porting checks nobody consumes, and move the deps under cfg(unix) so
windows builds stop resolving the PKCS#11 stack entirely.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:22:02 +08:00
Fini 30b11f3584 fix(collab): clamp kernel keepalive to whole seconds
socket2 truncates sub-second durations to zero and Linux rejects
TCP_KEEPIDLE=0 with EINVAL, so any sub-second heartbeat config broke
every connection at the socket; macOS silently kept the 7200s default
instead. os_keepalive_period() rounds the period up to the kernel's
whole-second granularity with a 1s floor, and a getsockopt readback
test pins what the kernel actually holds on both platforms.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-08-09 02:22:02 +08:00