Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
125 commits
Select commit Hold shift + click to select a range
229fe53
feat(otel): add reversible opt-in agent setup and wizard step
chhhee10 Oct 10, 2026
0eaf89e
feat(otel): add durable loopback relay and per-agent transcript lanes
chhhee10 Oct 10, 2026
240aec6
docs(otel): link changelog to draft PR 880
chhhee10 Oct 10, 2026
aa5c771
fix(ci): update vulnerable existing build and SDK test dependencies
chhhee10 Oct 10, 2026
df1ee96
docs(otel): clarify metrics acknowledgement in paired server
chhhee10 Oct 10, 2026
45195cc
feat(tui): palette for the 2026-10 screen language
chhhee10 Oct 10, 2026
157670f
feat(tui): building blocks for the 2026-10 screen language
chhhee10 Oct 10, 2026
fe69104
feat(config): remember which agents failproofai traces
chhhee10 Oct 10, 2026
78825ee
feat(policies): one shared answer to "is this machine enforcing?"
chhhee10 Oct 10, 2026
f3fc419
feat(tui): pickers in the 2026-10 screen language
chhhee10 Oct 10, 2026
2e591b2
fix(otel): preserve agent settings and require real Copilot installation
chhhee10 Oct 10, 2026
d647636
feat(otel): add optional durable TypeScript and Python span exporters
chhhee10 Oct 10, 2026
b6b2aa6
fix(otel): initialize delivery before collector startup milestone
chhhee10 Oct 10, 2026
7f9e3d8
feat(help): every help page in the 2026-10 screen language
chhhee10 Oct 10, 2026
30fb5ff
test(otel): isolate optional-library and initial-render assumptions
chhhee10 Oct 10, 2026
247bdb3
feat(daemon): agent selection gates collection; scoped backfill requests
chhhee10 Oct 10, 2026
e9883dd
feat(tui): the cloud-key prompt, and a helper to collapse sudo's prompt
chhhee10 Oct 10, 2026
a28aa7f
feat(config): the v2 setup flow — key, agents, apply
chhhee10 Oct 10, 2026
b0d21a8
feat(policies): the policies screens in the new language
chhhee10 Oct 10, 2026
a944780
feat(config): status and pause in the new language
chhhee10 Oct 10, 2026
9fbb6bc
feat(launch): the dashboard launch screen in the new language
chhhee10 Oct 10, 2026
0d1086b
feat(audit): per-agent progress and the audit screens in the new lang…
chhhee10 Oct 10, 2026
abc2a5a
feat(backfill,config): traced agents only, --agents, --oss
chhhee10 Oct 10, 2026
97a0aac
fix(daemon): a config edit right after a backfill was never applied
chhhee10 Oct 10, 2026
2376736
fix(config): --disconnect no longer sends you to restart the daemon
chhhee10 Oct 10, 2026
e168578
fix(otel): lazy imports, isolated setup and precise local configuration
chhhee10 Oct 10, 2026
f6a346d
fix(policies): show that the pre-pack shim still enforces builtins
chhhee10 Oct 10, 2026
ce94594
feat(cli): flush, migrate, update and uninstall results in the new look
chhhee10 Oct 10, 2026
2347d24
feat(hooks): installs and removals keep the agent selection true
chhhee10 Oct 10, 2026
32c77ed
test(cli): pin the new failure shape of action results
chhhee10 Oct 10, 2026
a96352c
feat(policies): show --releases and the empty picker in the new look
chhhee10 Oct 10, 2026
c5a2c80
fix(config): an unattended re-run keeps a saved empty selection
chhhee10 Oct 10, 2026
30611bf
feat(config): --connect, --disconnect and rename results in the new look
chhhee10 Oct 10, 2026
75d57c4
feat(notices): errors, notices and the one-off screens in the new lan…
chhhee10 Oct 11, 2026
3273d1b
feat(policies): --install and --uninstall results in the new look
chhhee10 Oct 11, 2026
638b297
fix(config): renaming an unconnected machine points at config, not a …
chhhee10 Oct 11, 2026
40188be
feat(jev): setup, test, models and remove results in the new look
chhhee10 Oct 11, 2026
cc26fda
chore(tui): retire the helpers no screen uses any more
chhhee10 Oct 11, 2026
5a96d2c
docs(changelog): one Fixes list, features in reading order
chhhee10 Oct 11, 2026
6355a4d
fix(cli): config and backfill argument errors in the same shape as th…
chhhee10 Oct 11, 2026
82e20b2
test: two tests that failed under load for reasons of their own
chhhee10 Oct 11, 2026
ca8edc3
feat(config): the first-run hints in the new look
chhhee10 Oct 11, 2026
8fb9798
docs(changelog): name the PR, #881
chhhee10 Oct 11, 2026
df1cfbc
chore(deps): clear the advisories OSV-Scanner flags, for the beta
chhhee10 Oct 11, 2026
d696033
test(hooks): two handler tests stop writing untraced notes into the r…
chhhee10 Oct 11, 2026
91d7654
test(hooks): two more suites stop writing into the real ~/.failproofai
chhhee10 Oct 11, 2026
e727018
test(uninstall): pin the options the real command passes removeHooks
chhhee10 Oct 11, 2026
b409fc3
test(tui): restore the logomark shape checks against screenKit().logo()
chhhee10 Oct 11, 2026
a285f2d
test(hooks): an untraced agent's hook event is still evaluated, never…
chhhee10 Oct 11, 2026
cfe1d60
test(e2e): policies --all can now fail on its folding claim
chhhee10 Oct 11, 2026
f58a1c6
test(config): isolate FAILPROOFAI_HOME in the wizard tests, not just …
chhhee10 Oct 11, 2026
b805737
fix(policies): a refused Jev-only pack no longer reads as enforcing
chhhee10 Oct 11, 2026
d9d3c7d
fix(config): an agent traced again starts fresh on every path
chhhee10 Oct 11, 2026
ab1b2e7
test(agents): a removal with no saved list records this machine's agents
chhhee10 Oct 11, 2026
4be7042
fix(config): a bad cloud URL stops only a run that would send a key
chhhee10 Oct 11, 2026
4b65641
fix(config): setup's summary names a pack that failed to load
chhhee10 Oct 11, 2026
bdd27c5
fix(config): the AGENTS step says what will change, not what did
chhhee10 Oct 11, 2026
d90ae2e
docs(config): say what configure_applied sends
chhhee10 Oct 11, 2026
bca58f5
fix(daemon): apply a request that lands mid-tick before the new colle…
chhhee10 Oct 11, 2026
c60e397
test(dashboard): unticking with no saved list records this machine's …
chhhee10 Oct 11, 2026
df13acc
fix(cli): did-you-mean no longer answers --uninstall and unpause with…
chhhee10 Oct 11, 2026
49c896c
fix(cli): a failed update, migrate or flush --wait marks the line tha…
chhhee10 Oct 11, 2026
9621406
fix(cli): config no longer drops --machine-label, and takes --send-tr…
chhhee10 Oct 11, 2026
6a4cacc
chore(cli): drop the --status --verbose promise nothing keeps
chhhee10 Oct 11, 2026
d28bc84
fix(tui): no escape codes in a redirected stderr
chhhee10 Oct 11, 2026
189c233
fix(cli): backfill --dry-run hands over the window it previewed
chhhee10 Oct 11, 2026
2ba1492
fix(tui): never cut a collapsed summary or a help option, and answer …
chhhee10 Oct 11, 2026
3dcf326
fix(cli): the shared policies help says which lane takes -c
chhhee10 Oct 11, 2026
480446f
docs(changelog): agent-list and --machine-label entries match the rev…
chhhee10 Oct 11, 2026
800ca75
fix(daemon): claim a backfill request by rename, so a later write is …
chhhee10 Oct 11, 2026
a39135c
Merge feat/tui-ux into feat/otel and port OTEL setup to config v2
chhhee10 Oct 11, 2026
e5870a7
test(otel): account for additive delivery in config v2 collector checks
chhhee10 Oct 11, 2026
76a749f
fix(otel): derive each status dot from its own state
chhhee10 Oct 11, 2026
52020af
fix(otel): keep OpenTelemetry out of `failproofai config`
chhhee10 Oct 12, 2026
0ffc7c1
test(otel): VS Code detection tests no longer depend on the machine
chhhee10 Oct 12, 2026
d15cfb7
chore: bump version to 1.0.11-beta.1
chhhee10 Oct 12, 2026
7f536f2
Merge remote-tracking branch 'origin/feat/tui-ux' into feat/otel
chhhee10 Oct 12, 2026
c6f5b2a
fix(launch): drop Next's "Running next.config took" line under the sc…
chhhee10 Oct 12, 2026
0791714
fix(status): config --status drops the dashboard, today and per-agent…
chhhee10 Oct 12, 2026
a30d40a
Merge remote-tracking branch 'origin/feat/tui-ux' into feat/otel
chhhee10 Oct 12, 2026
aff42d3
chore(deps): quinn-proto 0.11.18 for RUSTSEC-2026-0338 to -0342
chhhee10 Oct 12, 2026
5c37ac9
Merge remote-tracking branch 'origin/feat/tui-ux' into feat/otel
chhhee10 Oct 12, 2026
4c1ea8d
fix(otel): drop Gemini CLI; OTEL covers only agents failproofai supports
chhhee10 Oct 13, 2026
8e4410e
chore(deps): ruzstd 0.9.1 for RUSTSEC-2026-0344
chhhee10 Oct 13, 2026
7559143
Merge remote-tracking branch 'origin/feat/tui-ux' into feat/otel
chhhee10 Oct 13, 2026
7174e0d
test(otel): relay e2e waits for the health file, and gives a reply 10s
chhhee10 Oct 13, 2026
f909d34
fix(daemon): reset the cloud-policy rollback floor when the cloud ide…
chhhee10 Oct 14, 2026
f83320b
docs(changelog): reference #899
chhhee10 Oct 14, 2026
43715fc
fix(codex): put user-scope hooks where Codex runs them without trust
chhhee10 Oct 14, 2026
6503db1
Merge remote-tracking branch 'origin/feat/tui-ux' into feat/otel
chhhee10 Oct 14, 2026
9ceeab8
fix(otel): Codex runtime spans create empty sessions; export conversa…
chhhee10 Oct 14, 2026
fae5c90
fix(otel): enable overwrote prompt opt-outs; preserve explicit Codex …
chhhee10 Oct 14, 2026
fc5a2f8
fix(otel): resumed tailers replayed OTEL rollouts; persist an EOF res…
chhhee10 Oct 14, 2026
2c6c981
fix(otel): enable raced daemon config polling; await applied collecto…
chhhee10 Oct 14, 2026
64e7d6e
test(otel): cover logs-only migration, prompt privacy and transcript …
chhhee10 Oct 14, 2026
bebdd8c
docs(otel): remove unsupported Gemini enable and explain Codex handoffs
chhhee10 Oct 14, 2026
7e36e6f
fix(disconnect): cached desired state rebuilt old policies; remove it…
chhhee10 Oct 14, 2026
b76a2f5
fix(connect): stale capability keys survived reconnect; reconcile slo…
chhhee10 Oct 14, 2026
8a0ccd9
fix(org-switch): global cursors and queues leaked history; enforce re…
chhhee10 Oct 14, 2026
a33832d
fix(ci): rustls 0.23.45 has new advisories; update lock to patched 0.…
chhhee10 Oct 14, 2026
6bf1ec3
test(codex): protocol tests missed headless trust gate; prove managed…
chhhee10 Oct 14, 2026
1a4599a
test(codex): introspect fixture used wrong field names; assert the co…
chhhee10 Oct 14, 2026
bddb987
Merge origin/main into feat/tui-ux
chhhee10 Oct 15, 2026
d93bcfd
chore: bump version to 1.0.11-beta.2
chhhee10 Oct 15, 2026
cb534c5
Merge feat/tui-ux into feat/otel
chhhee10 Oct 15, 2026
c3a3b2d
Merge remote-tracking branch 'origin/fix/cloud-policy-floor-identity'…
chhhee10 Oct 15, 2026
b9ab364
Merge remote-tracking branch 'origin/feat/tui-ux' into feat/otel
chhhee10 Oct 15, 2026
b7fefc8
fix(config): drop two lines from the Done screen
chhhee10 Oct 15, 2026
3aac37c
fix(connect): only an org switch draws the recording boundary
chhhee10 Oct 15, 2026
0e11d77
chore: bump version to 1.0.11-beta.3
chhhee10 Oct 15, 2026
d567519
fix(otel): refuse to enable while the org has OTel ingest off
chhhee10 Oct 15, 2026
c1b4225
docs(otel): enable writes nothing while org ingest is off
chhhee10 Oct 15, 2026
0aa8b92
chore: bump version to 1.0.11-beta.4
chhhee10 Oct 15, 2026
9b8aded
fix(status): drop the codex row when hooks run from the system config
chhhee10 Oct 15, 2026
e7c3adf
fix(otel): Claude Code's auth header was refused with 401
chhhee10 Oct 15, 2026
e45bb2e
fix(otel): an org change now archives the OTLP relay's queue too
chhhee10 Oct 15, 2026
d6a054c
fix(otel): refuse to write or print the key for a plain-http ingest URL
chhhee10 Oct 15, 2026
fe995ec
fix(otel): a network outage no longer poisons OTLP batches
chhhee10 Oct 15, 2026
1403e04
fix(sdk-python): a blank FAILPROOFAI_HOME no longer means the cwd
chhhee10 Oct 15, 2026
999676c
fix(cloud): redrive refused batches only after the new key is on disk
chhhee10 Oct 15, 2026
9f23606
chore: bump version to 1.0.11-beta.5
chhhee10 Oct 15, 2026
a510d8e
fix(daemon): a collector slow to stop no longer ends collection for good
chhhee10 Oct 15, 2026
e9485f8
fix(sdk-python): keep the resolver shape the TS SDK's spool contract …
chhhee10 Oct 15, 2026
3032cf1
fix(sdk-ts): a blank FAILPROOFAI_HOME no longer means the cwd
chhhee10 Oct 15, 2026
585db60
chore(release): 1.0.11, and the SDK changelog sections for their next…
chhhee10 Oct 15, 2026
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:
# it produced was the daemon cross-compile, and only because it touched
# `crates/`. A PR that cannot go red is not a reviewed PR.
pull_request:
branches: [main, feat/fp-cli]
branches: [main, feat/fp-cli, feat/tui-ux]

concurrency:
group: ci-${{ github.ref }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/osv-scanner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ name: Supply Chain

on:
pull_request:
branches: [main]
branches: [main, feat/tui-ux]
push:
branches: [main]
schedule:
Expand Down
111 changes: 111 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

66 changes: 63 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ resolver = "3"
members = ["crates/*"]

[workspace.package]
version = "1.0.11-beta.0"
version = "1.0.11"
edition = "2024"
license-file = "LICENSE"
repository = "https://github.com/FailproofAI/failproofai"
42 changes: 17 additions & 25 deletions __tests__/audit/audit-cli-help.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ describe("audit --help", () => {
// it. Repeating the prefix on every row cost 17 of the 80 columns and was
// what forced the descriptions down to four words a line.
expect(text).toContain("failproofai audit");
expect(text).toContain("Scan your agents' history and open the results");
for (const command of [
"(bare)",
"--schedule [days]",
"--no-schedule",
"--status",
Expand All @@ -66,8 +66,13 @@ describe("audit --help", () => {
expect(plain(render(false))).not.toContain("--scheduled");
});

it("keeps the local-only promise the docs also make", () => {
expect(plain(render(false))).toMatch(/runs on this machine/i);
it("is usage, options and examples, and nothing else", () => {
// Decision D6 of the 2026-10 redesign removed explanatory and privacy
// lines from help pages; the audit's local-only promise went with them.
const headings = plain(render(false))
.split("\n")
.filter((l) => /^[A-Z][A-Z ]+$/.test(l));
expect(headings).toEqual(["USAGE", "OPTIONS", "EXAMPLES"]);
});

it.each([true, false])("aligns and fits 80 columns with color=%s", (color) => {
Expand All @@ -77,27 +82,14 @@ describe("audit --help", () => {
expect(line.length).toBeLessThanOrEqual(80);
}

// Every command row and every continuation line shares one description
// column. Derive it from the first row rather than restating a constant,
// so this fails on drift instead of being updated to match it.
const first = lines.find((l) => l.trim().startsWith("(bare)"));
expect(first).toBeDefined();
const descCol = first!.indexOf("Scan your session history");
expect(descCol).toBeGreaterThan(0);

const continuations = lines.filter(
(l) => l.startsWith(" ".repeat(descCol)) && l.trim().length > 0,
);
// A NON-VACUITY floor, not a layout assertion. The real check is the loop
// below — every continuation starts exactly at `descCol` — and this only
// proves it ran over something. Deliberately well under the count the
// current copy produces: pinning it to the exact number is what made this
// line fail twice for wording changes that improved the screen, once when
// dropping the `failproofai audit` prefix widened the column and again
// when `--email <address>` moved to a row of its own.
expect(continuations.length).toBeGreaterThanOrEqual(2);
for (const line of continuations) {
expect(line[descCol]).not.toBe(" ");
}
// Every option row shares one description column. Measured per row rather
// than against a constant, so this fails on drift instead of being updated
// to match it.
const optionRows = lines.filter((l) => /^ {2}-/.test(l));
// A non-vacuity floor: the loop below must have run over the real rows.
expect(optionRows.length).toBeGreaterThanOrEqual(4);
// The first character after the name's two-space gap.
const columns = new Set(optionRows.map((l) => l.search(/(?<=\S {2,})\S/)));
expect(columns.size).toBe(1);
});
});
3 changes: 2 additions & 1 deletion __tests__/audit/audit-cli-telemetry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ describe("failproofai audit telemetry", () => {
total_hits: 2,
findings: 0,
});
expect(h.launch).toHaveBeenCalledWith("start");
// Audit's hand-off prints its own lines, so the dashboard starts with no launch screen.
expect(h.launch).toHaveBeenCalledWith("start", { screen: false });
expect(exitInfo).toBeNull(); // happy path never exits — launch() keeps the process alive
});

Expand Down
62 changes: 47 additions & 15 deletions __tests__/audit/audit-cli.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
// @vitest-environment node
/**
* Unit coverage for the `failproofai audit` command's pure pieces:
* • AUDIT_STAGES stays in lockstep with the dashboard's RunProgress STAGES
* (the CLI and dashboard must tell the same four-step story).
* • AUDIT_STAGES stays in lockstep with the dashboard's RunProgress STAGES.
* The CLI no longer animates those four stages — by decision D16 it draws
* real per-agent bars — but it still names its discovery phase with the
* first one, so the two must keep using the same words.
* • buildSummary renders the right post-run line(s) for clean / mixed /
* singular results.
* singular results, in the 2026-10 screen language: a `✓` sentence with a
* full stop, then one indented sentence about what your policies cover.
*
* The server-launch + browser-open path is a thin wrapper over the existing
* `launch()` and is exercised by the build/smoke test, not here.
* The screen itself, and the hand-off to the dashboard, are covered in
* audit-screen.test.ts.
*/
import { describe, it, expect } from "vitest";
import { readFileSync } from "node:fs";
Expand Down Expand Up @@ -71,9 +74,9 @@ describe("buildSummary", () => {
totals: { hits: 0, projectsWithHits: 0 },
}),
).map(stripAnsi);
expect(lines[0]).toContain("audit complete");
expect(lines[0]).toContain("1,240 tool calls across 18 sessions");
expect(lines[0]).toContain("4 projects");
// Was "✓ audit complete · 1,240 tool calls across 18 sessions · 4 projects"
// before the 2026-10 screen language made results sentences.
expect(lines[0]).toBe("✓ Scanned 1,240 tool calls across 18 sessions in 4 projects.");
});

it("shows a clean-run line when something was scanned but nothing flagged", () => {
Expand All @@ -83,17 +86,18 @@ describe("buildSummary", () => {
transcripts: { scanned: 2, skipped: 0, errors: 0, durationMs: 0 },
}),
).map(stripAnsi);
expect(lines.join("\n")).toContain("clean run");
// Was "clean run — nothing flagged. nice." before the redesign.
expect(lines).toEqual(["✓ Scanned 10 tool calls across 2 sessions.", " Nothing was flagged."]);
expect(lines.join("\n")).not.toContain("slipping through");
});

it("does NOT claim a clean run when zero events were scanned", () => {
const lines = buildSummary(result({ eventsScanned: 0 })).map(stripAnsi);
expect(lines.join("\n")).not.toContain("clean run");
expect(lines.join("\n")).not.toContain("Nothing was flagged");
expect(lines[0]).toContain("0 tool calls across 0 sessions");
});

it("splits slipping-through vs already-blocked findings", () => {
it("splits slipping-through vs already-covered findings", () => {
const lines = buildSummary(
result({
eventsScanned: 500,
Expand All @@ -108,9 +112,29 @@ describe("buildSummary", () => {
],
}),
).map(stripAnsi);
const joined = lines.join("\n");
expect(joined).toContain("3 patterns slipping through");
expect(joined).toContain("2 already blocked by your policies");
// "Covered", not the old "already blocked": a policy that is on may warn
// rather than block, so "blocked" over-claimed for every enabled warn-*.
expect(lines[1]).toBe(" 3 patterns are slipping through, and 2 are already covered by your policies.");
});

it("says only the half that applies", () => {
const slippingOnly = buildSummary(
result({
eventsScanned: 5,
totals: { hits: 2, projectsWithHits: 1 },
results: [count({ source: "builtin", enabledInConfig: false }), count({ source: "audit-detector" })],
}),
).map(stripAnsi);
expect(slippingOnly[1]).toBe(" 2 patterns are slipping through.");

const coveredOnly = buildSummary(
result({
eventsScanned: 5,
totals: { hits: 1, projectsWithHits: 1 },
results: [count({ source: "builtin", enabledInConfig: true })],
}),
).map(stripAnsi);
expect(coveredOnly[1]).toBe(" 1 pattern is already covered by your policies.");
});

it("uses singular nouns for counts of one", () => {
Expand All @@ -123,6 +147,14 @@ describe("buildSummary", () => {
}),
).map(stripAnsi);
expect(lines[0]).toContain("1 tool call across 1 session");
expect(lines.join("\n")).toContain("1 pattern slipping through");
expect(lines.join("\n")).toContain("1 pattern is slipping through");
});

it("paints only the glyph, and leaves the counts in the terminal's own ink", () => {
const painted = buildSummary(
result({ eventsScanned: 3, transcripts: { scanned: 1, skipped: 0, errors: 0, durationMs: 0 } }),
{ color: true },
);
expect(painted[0]).toMatch(/^\x1B\[[0-9;]+m✓\x1B\[0m Scanned 3 tool calls across 1 session\.$/);
});
});
Loading
Loading