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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion console/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,11 @@ open for the selected row as you move with `j` / `k` or switch folders. On a
phone the folders are a row of tabs and Command details open as a bottom sheet. Without
JavaScript, rows show in full, with their own Dismiss and logs links.

Command details give the high-level view first: one line per recorded fact,
Command details open with the Inbox subject and the full issue title, before
Stop and Dismiss. The subject links to the issue, and long titles wrap. A
Command without an issue shows its Inbox subject alone; an unknown issue title
says “(title unavailable)”. The same recorded title is in
`thirdshift show <command> --json`. Below the heading is one line per recorded fact,
label then value. A long value, or a fact with its own facts, such as stages,
the dispatched Run or run metrics, starts folded behind a one-line preview (a
title, a name or a count); open it to drill down, as deep as the record goes.
Expand Down
32 changes: 32 additions & 0 deletions console/tests/inbox.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,38 @@ async function showDetails(page, row) {
await row.locator(':scope > .row-head > .state').click();
}

test('Command details opens with the Inbox subject and full title above its actions', async t => {
for (const width of [1280, 390]) {
const page = await pageFor(t, { viewport: { width, height: 844 } });
await page.goto(origin);
const row = page.locator('#inbox > [data-group="needs you"] .command').first();
const subject = await row.locator(':scope > .row-head > .subject').textContent();
const title = await row.locator(':scope > .row-head > .issue-title').textContent();
await showDetails(page, row);
const pane = page.getByRole('region', { name: 'Command details', exact: true });
const heading = pane.getByRole('heading', { level: 2 });
await heading.getByText(title, { exact: true }).waitFor();
assert.equal(await heading.getByRole('link').textContent(), subject);
assert.equal(await heading.getByRole('link').getAttribute('href'),
await row.locator(':scope > .row-head > .subject a').getAttribute('href'));
assert.equal(await heading.textContent(), `${subject} ${title}`);
assert.equal(await heading.locator('script').count(), 0);
assert.equal(await pane.getByRole('link', { name: /^Issue #/ }).count(), 0);
assert.equal(await heading.evaluate(el => Boolean(el.compareDocumentPosition(
el.parentElement.querySelector('form[data-dismiss]')) & Node.DOCUMENT_POSITION_FOLLOWING)), true);
const wrapped = await heading.locator('span').evaluate(el => ({
height: el.getBoundingClientRect().height,
lineHeight: parseFloat(getComputedStyle(el).lineHeight),
fits: el.scrollWidth <= el.clientWidth,
whiteSpace: getComputedStyle(el).whiteSpace,
}));
assert.ok(wrapped.height > wrapped.lineHeight, JSON.stringify({ width, wrapped }));
assert.equal(wrapped.fits, true);
assert.equal(wrapped.whiteSpace, 'normal');
await page.close();
}
});

test('inline Dismiss leaves Needs you without opening details and offers Undo on a phone', async t => {
const page = await pageFor(t, { viewport: { width: 390, height: 844 } });
await page.goto(origin);
Expand Down
1 change: 1 addition & 0 deletions site/architecture/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ <h1>Architecture</h1>
<p><code>thirdshift experiment <wbr>--release <wbr>&lt;commit&gt;</code> compares the content commit with the last release tag on touched suites. The release script waits for rechecks and asks permission for a fail or inconclusive Verdict before pushing. Reruns reuse the Experiment, including before opening a pushed branch's PR. It refuses different main content before pushing or merging and checks the merged tree before tagging, so the release cannot contain unmeasured changes. No touched suite or a last release without the Trial flag carries on with a note. <a href="/docs/#releasing">Releasing</a>.</p>
<p>Each Ticket run's own process saves its <strong>Run metrics</strong> at completion in its Command record, including Ticket runs and Base fixes: outcome, Repairs, CI rounds, Unaddressed findings by Standards, Spec and Security, stage times against their Usual ranges, and minutes by stage and in total. Unaddressed findings counts retain Delivery's final PR body and the Security review's count, so later edits do not change them. Each Session stage keeps the review's Standards and Spec finding counts from its summary, with missing counts left missing. <code>thirdshift show &lt;command&gt; --json</code> includes these retained facts. The Console reads the same details, showing each Ticket run's available metrics and marking unavailable signals as Missing. <code>thirdshift metrics &lt;command&gt; [--json]</code> reads a Ticket run with its children; without a Command it groups weekly trends by repository, Harness, requested Model and Effort, Ticket run kind and session kind. Week and repository filters narrow that view. Historical metrics are computed from Command records without writing back, and missing signals stay missing. No combined score is produced.</p>
<p>Run metrics and Experiments share one Session usage module for tokens by observed Model and Harness-reported cost. Its normalized-event and retained-span readers resolve call amendments and partial totals, retaining fields a total omits. Input includes cache reads and writes. Reported Session cost wins; otherwise each Model's reported cost replaces its call costs. The metrics reader separately counts tool errors, permission denials, killed work and repeated tool calls within that session. Completion metrics sum these by session kind, and weekly Session trends split by the Harness version from each trace's resource. Saved completion metrics stay fixed. Historical metrics also read converted Session traces, leaving missing traces and unreported measurements missing.</p>
<p>Command details opens with the same subject and issue title as the Inbox, before Stop and Dismiss. The subject links to the issue and long titles wrap. Unknown titles say “(title unavailable)”; Commands without an issue show only their Inbox subject. The recorded issue title comes from the same details JSON as <code>thirdshift show &lt;command&gt; --json</code>.</p>
<p>Command details reconstruct Session signals only for the selected Command and its nested Ticket runs and dispatches; all records still contribute to Usual ranges and health. Weekly metrics read signals for every Command. A bounded memory cache retains derived signals by the resolved Session trace's path, length, modification time and file identity, checking the identity again after parsing. Disposable <code>*.signals-cache.json</code> files beside each Session trace also retain them across CLI processes and Console restarts. Per-trace locks coordinate readers. Appends, replacements and a different binary invalidate the saved signals; an unwritable cache falls back to parsing. Historical Command records are never rewritten.</p>
<p>Confirmed Bug origins are joined when metrics are read, from local Scoring checkpoints under <code>~/.thirdshift/evals/scoring/</code>. The latest window's decision for each bug wins. Its retained local Ticket run identity selects the exact Ticket run; cross-host pull-request attribution selects the latest local Ticket run for that URL, as in Scoring, so Continuations never multiply a count. The Scoring data covers both hosts; metrics read no remote records. Late confirmations count in the Ticket run's start week, corrections move the count to the settled pull request, and rejected or unsettled origins count for nothing. Command details in <code>show --json</code> and the Console share this projection and reconstruct available historical metrics. Command records are never rewritten.</p>
<p>The same reader checks each session's shell commands against one code-owned destructive-command pattern list. Force pushes, shared-branch history rewrites and recursive deletes outside the Worktree are retained with their commands in Run metrics, with flag counts in weekly Session trends. This check is advisory: it never blocks or fails a Ticket run.</p>
Expand Down
1 change: 1 addition & 0 deletions site/how-it-works/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ <h1>How thirdshift works</h1>
<p>Run metrics and Experiments share Session usage rules. Partial totals replace only reported fields, preserving known input, cache counts and cost. Input includes cache reads and writes. A reported Session cost wins; otherwise each Model's reported cost replaces its call costs. Missing measurements stay missing, and saved completion metrics stay fixed.</p>
<p>CLI log following and the Console keep reading through the captured writer's exit, including final output written after a Command's outcome is recorded. An exact Session trace finishes when its writer exits or its owning record moves to a later Session.</p>
<p>The <strong>Console</strong> shows this machine's Commands, opening on its <strong>Inbox</strong>. Beside the CLI, you can watch their stages and logs there. Open Command details for a Ticket run's metrics, including available historical signals and late Bug-origin confirmations; unavailable signals say Missing. Stages keeps each Spec run with its Ticket runs, newest-started groups first, filling the window's width beside any open Command details: scroll right for older runs, or switch from All runs to By repository for one grid per repository, each folded to its name and run count until opened. When a later Command takes up an issue, the earlier row stays in Resolved for <code>inbox.days</code> from that Command's start, saying why it left.</p>
<p>Command details opens with the Inbox subject linked to the issue and its full title, above Stop and Dismiss. Long titles wrap; an unknown title says “(title unavailable)”. A Command without an issue shows its Inbox subject alone. The recorded issue title is also in <code>thirdshift show &lt;command&gt; --json</code>.</p>
<p>Command details and <code>thirdshift show &lt;command&gt; --json</code> read historical Session signals only for that Command and its nested work. The CLI and Console reuse derived signals while each Session trace is unchanged, including across process restarts, reading it again after an append, replacement or binary update. If a details request has not finished within 10 seconds, the pane says “Details unavailable. Reload to try again.”</p>
<p>The Console reads ready rows' pull requests and Needs you rows' issues and pull requests from GitHub in the background once a minute, even with no pages open. A changed answer wakes open Inbox pages; an unchanged answer leaves them idle. <code>thirdshift status</code> uses the same kept answer and reads GitHub itself only when it is missing or five minutes old. An ended Needs you row leaves once its issue closes or its pull request merges; a Triage row also leaves when <code>needs-triage</code> is removed. Architect and Security runs use the issue and pull request of the Spec run or Ticket run they dispatched. A pull request closed unmerged leaves a failure waiting while its issue is open. Live Silent and Slow rows wait until their Commands end. A merged or closed pull request takes its row out of Ready for review. Each exit says why, with Resolved counted from when GitHub shows it settled. Rows GitHub can speak for keep waiting however old they are. Reopening brings back a row still in Done or Resolved; History keeps the last facts and is not read again. Dismissals hold through GitHub changes; Undo uses GitHub's latest kept facts. Rows with no issue or pull request, no Command record, no read yet or nothing GitHub can find still age out, straight to History.</p>
<p>When GitHub cannot answer, each row keeps the group its last facts give it; a row never read still goes by age. After five minutes without a good read, the Console and <code>status</code> warn with the last-answer time and latest failure. A successful read clears that warning. With fewer than 500 GraphQL points or 500 REST requests left, both skip reads until that quota resets and say why, leaving the rest to the factory. <code>status --json</code> gives the last-answer time and latest failure. These limits are fixed, with no User config settings.</p>
Expand Down
1 change: 1 addition & 0 deletions site/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ <h2 id="grows-title">Six workflows</h2>
<p>A <a href="/docs/#security-runs">Security run</a> audits the Base branch and records findings privately. It is report-only by default. With <code>security-fix</code>, it can fix a reproduced finding through a Ticket run or Spec run. With <code>security-close</code>, it can close a finding its fix merged, once its proof-of-concept test passes against the merged Base branch. An optional <a href="/docs/#security-review">Security review</a> checks each Ticket run's change, including Tickets and the Spec PR, and holds Self-merge if introduced findings remain unaddressed or the review is incomplete. Its Harness is checked before work; an unavailable review Harness falls back to the default Harness, with one fallback also allowed for a Harness error before its first turn.</p>
<p>Start passes with <code>thirdshift architect base main</code>, <code>thirdshift pickup base main</code> or <code>thirdshift secure base main</code>. Your operating system schedules them. <code>thirdshift status</code>, <code>thirdshift cronjobs</code> and the Console show when each repository's Pass last fired, including repeated skips. <a href="/docs/#architect-runs-every-few-minutes">Architect runs every few minutes</a> and <a href="/docs/#security-runs-on-a-schedule-with-fixing-allowed">Security runs on a schedule, with fixing allowed</a>. Both yield to work the Day shift shaped, and pause when a decision needs you.</p>
<p>The <a href="/docs/#console-installation">Console</a> opens on this machine's Inbox: Commands grouped by what needs you, with their stages and logs. Like a mailbox, each group is a folder beside one-line rows, and a clicked row's Command details open alongside. It reads ready rows' pull requests and Needs you rows' issues and pull requests from GitHub in the background once a minute; changed answers update open pages automatically. <code>thirdshift status</code> uses the same kept answer. Failed reads keep the last facts; after five minutes, both show when GitHub last answered and why the latest read failed. With fewer than 500 GraphQL points or REST requests left, both wait for the quota to reset, leaving the rest to the factory. An ended Needs you row leaves when GitHub shows its issue closed or its pull request merged; a Triage row also leaves when <code>needs-triage</code> is removed. A Finding leaves with “findings triaged” once all its private records are triaged on GitHub. A draft advisory without severity, or an open <code>security-finding</code> issue labelled <code>needs-triage</code>, still waits; a finding closed by its merged fix does not. Live Silent and Slow rows wait until their Commands end. A signed-in account can Stop or Requeue a live Command, just as from the CLI. Dismiss is always visible on the row in Needs you or Ready for review, beside Copy where available; <code>d</code> dismisses the selected row and selects the next. An inline <a href="/docs/#dismissing-a-row">Dismissal</a> shows a temporary notice with Undo; Command details also keeps Dismiss and Undo. Command details shows each CLI form: <code>thirdshift dismiss &lt;command&gt;</code> or <code>thirdshift dismiss &lt;command&gt; --undo</code>. A dismissed row returns for a new kind of problem or when it ends ready for review; each kind counts once for the whole row.</p>
<p>Command details opens with the Inbox subject linked to the issue and its full title, wrapping long titles above Stop and Dismiss. An unknown title says “(title unavailable)”; a Command without an issue shows its Inbox subject alone. The recorded issue title is also in <code>thirdshift show &lt;command&gt; --json</code>.</p>
<p>Above its groups, the Inbox counts the runs active on this machine. The Console's <a href="/docs/#host-resources">Resources page</a>, also available with <code>thirdshift resources</code>, shows host memory, CPU load and the User config limits that can delay new sessions.</p>
<p><a href="/docs/#experiments">Experiments</a> compare a Candidate with a Baseline on private Eval suites. <code>thirdshift experiment <wbr>&lt;candidate&gt;</code> builds both Arms and runs interleaved Trials with two workers, a fixed Bar and resume after interruption or a plan-limit warning. Trials and Graders run in bwrap sandboxes. Code Graders score mechanical checks offline from report packets and the prepared repository, then fresh model Graders score written criteria from blind packets with provider-only access. Graders cannot read raw Trial logs, Arm mappings or the Experiment’s other files. Results show the Verdict, wrong findings, borderline marks, paired intervals, each Arm's minutes, tokens by Model, reported cost and Void causes. Trials and Graders join the Command's Trace. Void Trials retry fresh up to twice; an exhausted task drops out of both Arms. More than a tenth dropped makes the Verdict inconclusive.</p>
<p>Experiments, including release Experiments, report approved Eval tasks missing Trial inputs before building either Arm. Before each Trial's session, the host prepares its repository's pinned Rust toolchain and listed components, sharing preparation across Trials with the same pin. An installation failure makes the Trial void with <code>session_unavailable</code>. Spec review Trials keep the production prompt and grade only the reports; their sandbox blocks <code>gh</code> calls and provides no PR service.</p>
Expand Down
2 changes: 2 additions & 0 deletions src/console/assets/console.css
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ nav { display: flex; flex-wrap: wrap; gap: 8px 20px; margin-top: 10px; }
a { color: var(--link); overflow-wrap: anywhere; }
.page-title { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin: 16px 0 8px; }
h2 { margin: 0; font-weight: var(--head-weight); }
.peek-heading { white-space: normal; overflow-wrap: anywhere; }
.peek-heading > span { display: block; }
code, .now, .subject, .plan { font-family: var(--font-mono); font-size: 12px; }
code { overflow-wrap: anywhere; }
/* A CLI line stays one line, cut off at the edge; Copy CLI copies all of it. */
Expand Down
Loading
Loading