mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-12 06:06:37 +03:00
f704fca29c73871417c19453aa8553852dec0bb6
15
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
377fb112b0 |
Pass the session key from detached idle-grace tests
Main's #469 tests POSTed /heartbeat and /answer without ?key=, which the gate now rejects, so those daemons looked dead. The e2e heartbeat counter also has to match pathname rather than a suffix, now that the URL carries the key. Written with AI assistance under maintainer direction. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
7982002dac |
Allow bare loopback Host/Origin on port 80, where browsers omit the suffix
Bugbot caught that the exact-match allowlists 403 every request on --port 80 because browsers drop the default-port suffix; other ports stay strict. Written with AI assistance under maintainer direction. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
2e075dc58c |
Gate the build-path flip behind the same session key and origin checks
An unauthenticated POST /build-path wrote the flip event that makes --wait instruct the agent to generate comps: same class as the /answer hole in #555. Written with AI assistance under maintainer direction. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
eaaecbd1fe |
Fix: require session key and origin/host checks on serve-question POSTs (#555)
Unauthenticated POST /answer copied steer into the agent ANSWER line. The handler now requires the detached session key and rejects foreign Origin and Host. Written with AI assistance under maintainer direction. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
3c6f53406b |
Fix: stop the direction page hanging forever after a re-roll (#469) (#530)
* Fix: stop the direction page hanging forever after a re-roll (#469) The re-roll leg of the decision-page protocol was documented only in serve-question.mjs's own header, so agents never ran --update and the open tab polled a round that could never arrive. Compounding failure modes: the page poll swallowed every error, the daemon's --timeout was an absolute guillotine that killed the server under a still-open tab, a choice posted to a dead server confirmed nothing, and refresh or Reload on an unresolved round resurrected heartbeats that held the daemon alive indefinitely. - new-work.md documents the re-roll leg: rerun concept-seed with --from/--reroll, deliver with --update on the same key, never --start a second server. - The page poll terminates and says why: eight consecutive fetch failures means the server is gone; the delivery deadline (the server's own --idle-grace, inlined into the page) passing means the hand never arrived. Both stop heartbeating. - The daemon's --timeout bounds only the wait for a page to open; once the page heartbeats, the server lives while the page does and exits after --idle-grace (default 600s) without a beat, including under --timeout 0. - Build this and Re-roll against a dead server fail loudly instead of silently swallowing the click. - The server tracks the window between a collected re-roll answer and the --update that replaces the round, and serves the page in waiting mode there, so a native refresh re-enters the same bounded wait instead of resurrecting dead cards; the in-page Reload button only revives a delivered hand. - --update is exempt from the headless gate and its liveness probe trusts a fresh heartbeat over a failed kill probe (sandbox EPERM is not death). Squash of the six review-round commits on this branch, rebased onto main after the decision-page revamp. AI assistance: prepared with an AI agent operating under maintainer instruction (abdulwahabone). Co-authored-by: Cursor <cursoragent@cursor.com> * Fix review findings: persist the replacement deadline, refuse unloadable hands A browser-native refresh of the waiting page re-entered the bounded wait with a fresh delivery deadline and an immediate heartbeat, so refreshing before each deadline expired could hold the daemon alive and keep --wait on WAITING indefinitely. The server now records when the re-roll or followup answer was collected, each served waiting page inherits only what remains of that one allowance, and a page served after the deadline renders stalled immediately and never starts its heartbeat. And a next hand the round could not load used to reload-loop the tab: GET /'s catch kept the file on disk, so /next-status stayed ready:true forever. --update now refuses a payload without a non-empty options array at the sender, and GET / discards an unloadable next file so the bounded wait resumes. AI-assisted (Cursor agent) under maintainer instruction. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix review finding: a stalled page recovers a late hand without a click The stall silenced heartbeats so the idle grace could reclaim the daemon, but that silence read as a closed tab: after a late --update, --wait saw the stale beat and reported PAGE CLOSED while the user sat on the Reload screen, so the agent abandoned the browser path the recovery UI exists for. The stall screen now keeps a beat-free /next-status watch that reloads into a delivered hand on its own (GET never beats, so an abandoned flow is still reclaimed), and --wait no longer concludes closure from a stale beat while an undelivered next hand sits on disk. AI-assisted (Cursor agent) under maintainer instruction. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix review finding: a delivered hand must not mask a closed page The mid-delivery suppression keyed on the next file existing, but a closed tab never claims that file, so an unconsumed delivery held --wait on WAITING indefinitely instead of reporting the closed flow. The suppression is now age-bound: a stalled page's watch reclaims a delivered hand within seconds, so a file still unclaimed after a 10s grace means no page is coming back and the stale beat reads as the closed page it is. AI-assisted (Cursor agent) under maintainer instruction. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix review finding: stamp the delivery clock at --update, not the copy --wait's mid-delivery grace reads the next file's mtime, but copyFileSync's timestamp behavior is the platform's business: a copy that preserves the source payload's older mtime would start the grace already spent and report PAGE CLOSED under a live stalled tab. --update now touches the delivered file itself, so delivery time is delivery time everywhere. AI-assisted (Cursor agent) under maintainer instruction. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix review findings: disable canon during the wait, validate --timeout The waiting and stall screens disabled only the re-roll buttons; the footer canon action stayed clickable, and a canon pick posted after --wait had consumed the re-roll could never be collected: it overwrote the answer, marked the table closed, and exited the daemon under the agent. Both disable sites now take the canon exit down with the re-roll buttons; a delivered hand reloads the page and serves it live again. And --timeout reached the lifetime timer unvalidated: NaN or a negative value disarmed the no-page exit and the daemon leaked. It now takes the default unless the value is a finite non-negative number, keeping 0 as the explicit wait-forever. AI-assisted (Cursor agent) under maintainer instruction. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix review finding: a second click must not renew the delivery deadline dealAgain left the re-roll and canon controls live through the answer POST and the 700ms fly-out, so a second click posted another re-roll and the server restamped awaitingNextSince, renewing the deadline this PR made non-renewable on refresh and on the stall screen. The controls now go quiet at the click itself, in dealAgain and in answer(), and the server stamps the allowance only on the transition into the wait, so a duplicate answer racing the disable keeps the first stamp. Regression coverage on both sides: the unit deadline test posts a duplicate re-roll mid-allowance and asserts the budget shrank instead of resetting, and the e2e stall test asserts both controls are disabled immediately after the click, before the fly-out. AI-assisted (Cursor agent) under maintainer instruction. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix review finding: a late delivery must survive its claim window --update could land a replacement hand after the stalled page went silent but moments before the daemon's idle deadline: the daemon exited before the page's 1.5s watch could claim the hand, orphaning a delivery --update had confirmed, and the next --wait reported a server failure. The idle exit now defers while an unclaimed next hand is younger than the claim grace --wait already reads (extracted as one shared constant), so the page's watch deals it and heartbeats resume; a file unclaimed past the grace still ends the daemon, bounded as before. Regression test: deliver at idle-deadline-minus-a-beat, assert the daemon survives past the deadline and serves the late hand. AI-assisted (Cursor agent) under maintainer instruction. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix review finding: the claim itself must hold the daemon The idle-exit hold read only the next file's freshness, but GET / deletes that file when it serves the claimed round, before the reloading page can post its first heartbeat: a lifetime tick in that gap saw no pending hand and a stale beat, and exited under the hand just claimed. GET / now stamps the claim when it consumes a pending hand, and the idle exit honors the same bounded grace from that stamp, so the reloading page gets its seconds to beat while an abandoned claim still ends the daemon at the grace. The claim-window regression test now also fetches after the claim, past another lifetime tick, and asserts the daemon survived the gap; verified it fails on the previous commit. AI-assisted (Cursor agent) under maintainer instruction. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix review finding: --wait must ride out the claim gap too The claim deletes the next file --wait's mid-delivery grace watches, and the reloading page has not beat yet, so --wait in that gap read the stale beat as PAGE CLOSED while the daemon was alive serving the dealt round, and the agent abandoned a browser session that had just recovered. GET / now persists the claim stamp into the per-key state file, and --wait's suppression honors it under the same bounded grace: a fresh claim stays WAITING, a claim nobody followed with a beat still reads as the closed page it is. Regression test drives --wait through the gap (claim with a stale beat: WAITING, not exit 4) and past it (backdated claim stamp: exit 4); verified it fails on the previous commit. AI-assisted (Cursor agent) under maintainer instruction. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Paul Bakaus <paul.bakaus@gmail.com> |
||
|
|
248a4a699a |
Retire the sketch era's wire name: the field is comp, sketch is an alias
The deliverable died in #545; the word survived as the decision-page payload's field name, annotated everywhere it appeared with the same compatibility apology. The page and the skill text ship together and payloads are per-session, so the compatibility burden is one input alias, not a frozen name. serve-question.mjs: the card field, the answer key, the schema docs, the --schema example, the help text, and every internal identifier (compSrc, data-comp, .media.comp-pending, img.comp, comp-note) now say comp; a payload declaring the legacy sketch key still renders and answers identically. new-work.md and the asset producer drop their wire-name parentheticals. The unit suite covers the canonical answer key coming back from a legacy-key payload; the new-work e2e's declined-card stray comp stays declared as sketch, which doubles as alias coverage. AI-assisted (Claude Fable 5), prepared for maintainer review. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
dbff0880e6 |
Decision page: full-fidelity comps, raise cycler, declined sizing, canon order, full card anatomy (#545)
* Polish the decision page: raise cycler, declined height, canon order, full card anatomy Field feedback from the first real rolls of the verdict-routed hand: - Several raises stacked on the assigned card blew it out of proportion. More than one raise now renders as a compact cycler: one visible, a counter, click or Enter advances. A single raise stays inline. - Declined cards inherited the row's stretch alignment, so a narrow card stood at the tallest contender's height, a strange stilt beside the hand. They now size to their content. - Deck order becomes a gradient of standing: contenders, then the canon, then declined dead last. The canon between full alternates and the demoted row reads as the familiar door rather than the last resort after the rejects. - Root cause of bare-bones challenger and canon cards in the field: the --schema example only gave the assigned card palette, materials, and risk, and models author payloads by imitating the example, so the "same anatomy on every card" instruction lost to it every time. The example now carries full anatomy on every card and the schema note says a card with no palette chips is an authoring gap, not a data gap. AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> * Decision cards carry full-fidelity comps instead of sketches Field verdict on the sketch contract: the sketches came back too simple to inform the choice, and generation takes the same time at any fidelity, so the deliberately-unfinished frame paid comp cost for sketch quality. The decision card's image is now that direction's north-star comp, produced under visualize.md's comp discipline (structure-led prompt, real name and content, no invented commercial claims), saved under .impeccable/mocks/ with its prompt sidecar. Fairness between cards comes from equal fidelity in each card's own grammar rather than shared unfinishedness. The chosen card's comp is never spent by the choice: on a comp-led build it enters the comp round as compositional option one (visualize.md now generates two variations beside it; a round arriving with no decision comp still renders all three), and on a code-led build it returns at the finish review as the critique reference. Produce order still front-loads a re-roll's spend onto the cards read first. serve-question keeps the sketch field's wire name for payload compatibility; docs, schema paths, shimmer labels, and the answer directive (CHOSEN COMP) speak comp. AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> * fix: address PR review bot findings on the comp round - Producer still forced sketches (cursor, high): the asset producer's Decision Sketches contract still mandated deliberately unfinished matte sketches, so the parallel path would keep shipping sketch-era images. The section is now Decision Comps: full-fidelity north-star comp, structure-led prompt, equal commitment across siblings, no invented claims, sidecar written. - Mocks collided with the approval check (cursor, high): decision comps now live under .impeccable/mocks/decision/, visualize.md scopes the no-approval finding to comp-round output, new-work.md states the unchosen hand implies no approval, and the code-led finish packet names the chosen decision comp as the critique reference in the approved-comp slot. - Raise cycler announces (greptile, both P1s): a visually hidden aria-live region reads out the newly active raise and its position on advance; initial render stays quiet. - Declined width in the vertical deck (cursor, medium): align-self: flex-start shrank declined cards to content width in the portrait column layout, where the cross axis is horizontal; they stretch there and keep content height in the row layout. AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> * fix: raise cycler tooltip and label name both input modes Copilot: the tooltip said Click while the control also answers Enter and Space; the title and a new aria-label now say activate/press Enter. AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> * fix: finish reviewer exempts decision comps from the approval check cursor[bot] follow-through: the reviewer's Persistence check still treated any comps under .impeccable/mocks/ as approval-gated, and the reviewer never reads visualize.md by design, so code-led and spent-hand rounds could draw a false skipped-approval finding. The check now scopes to comp-round comps, exempts .impeccable/mocks/decision/ as the direction round's dealt hand, and defines how a code-led build's decision comp is judged in the approved-comp slot: the critique reference, under the no-approved-comp fidelity rules plus what the image dared that the build did not. AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> * fix: the critique reference is its own reviewer input, not the approved-comp slot cursor[bot]: passing the code-led decision comp through the approved-comp slot dragged in that slot's obligations (inventory-first reading, the fidelity matrix, Truth's shipped-asset demand for every image-native region), which contradicts code-led's premise. The input contract now names it a separate labeled critique-reference input that nothing binding "the approved comp" touches, and Fidelity defines its treatment where the no-approved-comp rules live: provocation, not spec; no matrix, citations, or asset obligations; its dares enter material_fixes as ordinary fixes. AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> --------- Co-authored-by: Claude Code <noreply@anthropic.com> |
||
|
|
c70bcbf6b4 |
Direction round: verdict-routed hand, MY PICK card, salience parity, Safer/Bolder registers (#531)
* Route the direction hand by verdict, add the pick card, enforce salience parity The decision round previously rendered every dealt challenger as an equal full card whatever the weighing said, so a world that fused poorly (an underwater world dealt to a flower shop) sat at the same visual weight as the assigned direction, and concept-level fusion had no surviving output. Three changes, all presentation-layer; the dice, the assignment, and the two-axis weighing are untouched: - Verdict routing: the weighing closes with wins / competitive / declined per challenger, decided before any borrowing. Declined challengers render demoted (narrow, quiet, catalog art as a labeled thumb, "Adopt anyway"), reordered to the end of the deck by the page itself, still adoptable, never silently dropped. Donations return as named "raised by" lines on the assigned card: a declined challenger donates ambition and system discipline, never its clothes. - The pick card: one card for the model's top-ranked grounded candidate when the dice assigned another, kicker MY PICK, honest familiarity risk on its face. One card, never a ranked list, never the lead position; the anti-menu rule survives with exactly this carve-out. - Salience parity: a card's imagery weight is capped by the assigned card's. With a text-only assigned card (no image generation in the harness), full-bleed catalog heroes demote to labeled thumbs, so what looks important is the verdict's call, never rendering luck. serve-question payload gains additive fields (verdict, kept, raised); old payloads render unchanged. concept-seed's rendered instructions carry the verdict/donation contract and the pick-card carve-out. Covered by two Playwright tests in the new-work e2e suite (verdict routing + parity). Design exploration and rationale were worked through with the maintainer; research grounding is impeccable.style/research lessons 3-5. AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> * Add Safer/Bolder re-roll registers to the direction round The re-roll gains the user's steering wheel on the familiar-to-bold axis. The decision page renders two register buttons beside the plain re-roll (payload: reroll: { registers: ["safer", "bolder"] }; booleans still work), the answer carries the chosen register, and concept-seed gains --register. The design constraint that shaped the implementation: a register changes only what a round INSTRUCTS, never what it DEALT. The same key and reroll count reproduce the same deal whatever the register, so the exclusion chain never forks and the reproduction contract holds with no API change. - bolder: the dealt foreign forms become the whole hand, every challenger a full card; the first-dealt challenger leads (assignment by deal order, so the dice still choose). The pick card sits out; the canon stays. - safer: the round's dealt hand is spent unseen and stays excluded; the model presents its remaining conventional grounded candidates (at most three) plus the canon executed against named competitors. This is the one sanctioned lineup of the model's own ranked list, existing only by explicit user request. Works degraded (needs no catalog); bolder degrades to a plain grounded round, disclosed. Registers are user steering, never the model's to pre-select. Covered by a concept-seed unit test (same-deal invariant, validation) and a Playwright test (button, answer field, REGISTER directive). AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> * Add the execution-contract round: comp-led or code-led, chosen after the direction The build previously went comp-led for everyone, silently: a generated comp led and the build chased it, which produces the boldest compositions and also the measured worst-of-both-worlds failure (ambitious design landed poorly, no motion, fix rounds after). Models already defect from it by quietly skipping comp generation, which is unsanctioned code-led with no contract to catch it. This makes the fork explicit and both paths defection-proof: - Comp-led: the comp is law and non-optional once chosen; visualize.md and the comp-is-king build phases run as today. - Code-led: no comp of this page, skipped by contract rather than drift. The QUALITY BAR boards still calibrate finish, and the ambition moves into the written direction contract (FIRST VIEWPORT plus a named signature interaction and motion grammar), audited by the finish reviewer in behavior. Not a discount on commitment. Placement: a second round on the same open table, right after the direction lands. Sketches stay in the direction round (they pick the world); comps are what code-led skips (they bind the composition). The chosen world sets the default lead; the user flips freely; a standing preference recorded in PRODUCT.md skips the round on later surfaces; with no image generation there is no fork, code-led is the only path. Mechanism: serve-question gains payload-level followup: true, which keeps the detached server alive after a pick (exactly like re-roll), swaps the page to the loading hand instead of goodbye, marks the answer with followup: true so --wait keeps the table, and prints a FOLLOWUP OPEN directive telling the agent to deliver the next round via --update. Covered by a Playwright test driving the full two-round flow. AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> * fix: address PR review bot findings - Degraded safer register no longer contradicts itself (greptile, Copilot, cursor): the degraded template previously said "the assigned index is suspended; the user picks" and then emitted ASSIGNED INDEX, the mandatory build instruction, and the restated footer anyway. The degraded safer path now suppresses the assignment machinery entirely, matching the non-degraded safer round, and restates the user-picks behavior for truncated readers instead. - A declined card's declared sketch no longer renders a full media face (Copilot): the renderer ignores sketch slots on declined cards outright, so a stray sketch cannot buy back the salience the verdict took away. - Bolder rounds no longer carry the generic weighing instruction (cursor): it measures against the assigned grounded direction, which the bolder register suspends; a leader-relative variant weighs the fused challengers against the first-dealt leader instead. All three pinned by new assertions in tests/concept-seed.test.mjs and tests/new-work-e2e.test.mjs. AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> * fix: followup never arms the loading hand in blocking serve mode cursor[bot] caught a client/server disagreement: the page interpolated its FOLLOWUP constant from the payload alone, so a followup: true payload served in blocking mode (no --start) would leave the browser on a loading hand that nothing resolves, since a blocking server exits on any pick and has no update channel. The page constant is now armed only when the server is detached, blocking rounds get the goodbye screen as before, and new-work.md states that followup belongs only on a detached round; blocking and structured-tool channels run the build-path round as its own second question. Pinned in tests/serve-question.test.mjs. AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> * Add card-kind choice telemetry and the bolder routing disambiguation The choice ping previously fired only when a dealt catalog challenger won, so pick-share and canon-share had no denominator and the decision page's new spectrum could not be measured. The ping now fires once per resolved attended round on API-dealt rolls: --kind names which card class won (assigned / pick / challenger / canon), --chosen carries the catalog id only when a dealt challenger won, and --register rides along when the round came from a steered hand. Grounded candidates' names never leave the machine (the ping carries the kind alone), the legacy id-only shape stays valid, and DO_NOT_TRACK / IMPECCABLE_NO_TELEMETRY still disable the ping entirely. The seed's TELEMETRY block teaches the new invocation. Also the naming-collision guard: "bolder" said while a direction round is open routes to the Bolder hand register, never the bolder refinement command; one line each in bolder.md and new-work.md. The /api/chosen field additions land in a sister impeccable-site PR; the API ignores unknown fields meanwhile, so this is safe to ship first. AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> * fix: ping test survives a DO_NOT_TRACK shell cursor[bot]: the pingChosen unit test cleared only IMPECCABLE_NO_TELEMETRY, so a developer shell with DO_NOT_TRACK set failed the success-path assertions. The test now clears both, restores prior values in finally, and passes under DO_NOT_TRACK=1. AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> --------- Co-authored-by: Claude Code <noreply@anthropic.com> |
||
|
|
a37b3f6b02 |
Fix Windows question browser opening (#510)
AI assistance: Codex reproduced the issue, implemented the fix, and ran the validation described in the pull request. |
||
|
|
6c4620bf53 |
A sandboxed wait cannot signal the server, and EPERM was read as death
A codex session declared the decision board dead while the user was still reading it, then proceeded without their choice. The wait's liveness probe was process.kill(pid, 0) with every error treated as gone, but a sandboxed exec cannot signal a process outside its sandbox: EPERM arrives for a living server. Liveness now leads with the page's own heartbeat in the state file, falls back to the kill probe, and reads EPERM specifically as exists-but-unsignalable. The exit-2 message also stopped inviting the wrong recovery: it now states this is a server failure, not a user decision, and orders a restart and reopen, never an unattended proceed while the user's browser session is open. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
e6612ea8ef |
Page the deck on its long axis, and never let decoration hide the cards
Field-checked in a real browser, which surfaced three defects the DOM tests could not: the generic .media img display rule defeated [hidden] and floated an empty block over the shimmer and its sketching note; the deal animation left every card at opacity zero in an unfocused tab, because rAF throttling is real and decoration must never gate content; and the sketch poll's cache-busting query missed the anchored /img route, so a landed sketch kept shimmering forever. The grid is now a snap-scrolling deck: one row in a wide viewport, one column in a tall one, with edge arrows that appear only on overflow and page one card at a time. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d89ee5f87c |
Deal every card the same hand: anatomy, sketches, and the standing door
The decision page compared unlike things: the grounded direction was a wall of text beside curated catalog art, the catalog art read as a promise of the build, the weighing silently shrank the challenger set, and the standing exit hid in the footer under the cards it must not soften. Every card now shares one anatomy (thesis, palette chips, material tags, first viewport, case, risk), every dealt challenger is presented with the weighing written on it rather than applied to it, the catalog image rides picture-in-picture as labeled inspiration with the lightbox a click away, and canonCard renders the category standard as one honest, subordinate card. When image generation exists, each card declares a sketch slot the page polls: serve first, generate after, through one shared deliberately unfinished frame, so the comparison stays about direction instead of rendering luck. The asset producer takes the batch when subagents exist; the chosen sketch returns in ANSWER to seed at most one comp probe, and the comp round still renders its full set. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
ea68bb4722 |
Scope headless detection to the path that opens a browser
The new self-detection ran before mode dispatch, so it also caught --wait, --stop, and --schema. CI failed on the start/wait cycle test: --wait returned 2 (no browser) where the documented poll loop expects 3 (WAITING). Under CI=1 the suite went 4 pass / 2 fail; it is 6 / 0 now. Two of those modes were user-facing bugs, not just test breakage. --stop exited 2 without killing the daemon it was asked to kill, leaking a server process (verified: one daemon running, CI=1 --stop, still one). --schema only prints a payload example, and new-work.md tells the agent to read it before building a payload. Detection can only tell whether this process can auto-open a browser, not whether the user has one: SSH with a forwarded port and a harness with an in-app browser both have a browser and no DISPLAY. The file already treats serve-without-opening as first class, since --start spawns its own daemon with --no-open. So the check now gates acquiring a session, not managing or ending one. The blocking serve path still exits 2 on a headless box, with a test pinning that. Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
285ed7ef78 |
serve-question: schema discoverability and a non-blocking mode
Paul's two concerns with the blocking design. --schema prints the exact payload example so the model never guesses the shape (new-work.md points at it). And harnesses that cannot leave a shell blocked (or cannot open a browser while blocked) get a two-phase path: --start daemonizes the server and returns the URL plus a key immediately, --wait polls for the answer with exit 3 meaning ask again, exit 2 meaning the server died, and --stop for cleanup. The browser open happens from the detached server process, so it works even when the agent thread is short-lived. State lives under .impeccable/questions/. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
5612cdf45b |
Visual decisions and default visualization
Paul's design, three pieces: serve-question.mjs: the world decision presented as a themed page instead of a text prompt. The script serves an impeccable-styled option board (assigned direction leading with THE ROLL badge, dealt challengers as alternates carrying their QUALITY BAR cards, re-roll and steer built in), prints the URL, opens the browser, and blocks until the user chooses; the answer lands on stdout as ANSWER JSON, so the shell call itself is the wait and no harness machinery is needed. Local images are served by the ephemeral server; nothing leaves the machine. generate-image.mjs + context.mjs IMAGE_GEN_AVAILABLE: when an OpenAI key is in the environment, context reports that image generation works even without a harness-native tool (gpt-image-2, billed to the user's key, stated before first use; Google skipped by decision). Harness-native tools always win when present. new-work.md: visualize-before-build is now the default whenever any image generation exists, not a codex.md special case; the attended presentation prefers the visual decision page and falls back to the structured question tool. Evals keep the unattended path untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |