From 983ee9f18352ffd68be467551fb928e4e7517b93 Mon Sep 17 00:00:00 2001 From: Trinity Bee Date: Mon, 12 Oct 2026 04:18:34 +0000 Subject: [PATCH 1/2] Spec the Trinity GitHub App on top of the t27o install contract (#8997) specs/tools/mcp/trinity.t27: add the app section. The app proposes the t27o workflow PR on the installation event and reads scan reports on the workflow_run event; the install contract itself stays owned by specs/tools/t27o/install.t27 and is only called (use + install::), never restated (#8963). specs/tri/pipeline/workflow.t27: add the scan-report model the pipeline hands the app. A ScanReport carries the oracle evidence per spec; the golden verdict, refusal reason, and badge percentage are decided by the golden criteria (specs/tools/t27o/golden.t27) and only called (golden::refusal / refusal_name / is_golden / coverage_percent). The badge is the floored coverage: 5 of 8 evaluated specs reads 62. Verification: t27c parse and t27c typecheck pass on both files (0 errors); t27c test-report runs 13 new tests, 6 in trinity.t27 and 7 in workflow.t27, all non-vacuous (17 total: 13 new + 4 pre-existing, 0 failed, 0 vacuous). The golden-grep count across the two files is 35 (>= 4 required). No files outside the permitted boundary were touched. LLM/bee fallback stays opt-in only and is not referenced by these specs. Closes #8997 --- specs/tools/mcp/trinity.t27 | 60 +++++++++++ specs/tri/pipeline/workflow.t27 | 177 ++++++++++++++++++++++++++++++++ 2 files changed, 237 insertions(+) diff --git a/specs/tools/mcp/trinity.t27 b/specs/tools/mcp/trinity.t27 index a9c5d4eeab..ad384613d9 100644 --- a/specs/tools/mcp/trinity.t27 +++ b/specs/tools/mcp/trinity.t27 @@ -11,6 +11,8 @@ module tool_mcp_trinity; +use tools::t27o::install; + pub const KIND : str = "tool"; pub const FAMILY : str = "mcp"; pub const ID : str = "mcp/trinity"; @@ -55,3 +57,61 @@ test verify_trinity_constants { assert EXTERNAL == false; assert AGENTS[0] == "T"; } + +; --- Trinity GitHub App (#8997) ------------------------------------------------------------ +; The app installs the t27o workflow into a repository and reads the scan reports the +; workflow emits. The install contract (what is proposed, where it lands, what it ships) +; is decided by specs/tools/t27o/install.t27; the app only ever calls it, never restates +; it (#8963). The report model and the badge arithmetic are specified in +; specs/tri/pipeline/workflow.t27; this card only binds the app to the install contract +; and to the event the app reads reports on. + +pub fn app_installs_workflow_pr(event: str) -> bool { + return install::proposes_workflow_pr(event); +} + +pub fn app_workflow_pr_path() -> str { + return install::DEST_PATH; +} + +pub fn app_workflow_pr_criteria() -> str { + return install::CRITERIA_SPEC; +} + +pub fn app_workflow_pr_action() -> str { + return install::ACTION; +} + +pub fn app_reads_reports_on_workflow_run(event: str) -> bool { + return event == "workflow_run"; +} + +test app_installs_the_workflow_pr_on_the_installation_event { + assert app_installs_workflow_pr("installation") == true; + assert app_installs_workflow_pr("pull_request") == false; + assert app_installs_workflow_pr("workflow_run") == false; +} + +test app_workflow_pr_ships_the_t27o_workflow { + assert app_workflow_pr_path() == ".github/workflows/t27o.yml"; + assert app_workflow_pr_action() == "gHashTag/t27o-action@v1"; +} + +test app_workflow_pr_judges_specs_by_the_golden_criteria { + assert app_workflow_pr_criteria() == "specs/tools/t27o/golden.t27"; +} + +test app_reads_reports_on_the_workflow_run_event { + assert app_reads_reports_on_workflow_run("workflow_run") == true; + assert app_reads_reports_on_workflow_run("installation") == false; +} + +test app_proposes_nothing_on_unsubscribed_events { + assert app_installs_workflow_pr("push") == false; + assert app_installs_workflow_pr("schedule") == false; +} + +test app_webhook_handlers_do_not_cross { + assert app_installs_workflow_pr("workflow_run") == false; + assert app_reads_reports_on_workflow_run("installation") == false; +} diff --git a/specs/tri/pipeline/workflow.t27 b/specs/tri/pipeline/workflow.t27 index ab859f7ca9..3b6d3a4fbc 100644 --- a/specs/tri/pipeline/workflow.t27 +++ b/specs/tri/pipeline/workflow.t27 @@ -5,6 +5,7 @@ module Workflow; use base::types; use math::constants; + use tools::t27o::golden; // ═══════════════════════════════════════════════════════════ // 2. Types @@ -86,3 +87,179 @@ module Workflow; assert workflow_step_has_condition(step) == false; } + // ═══════════════════════════════════════════════════════════ + // Scan reports (issue #8997) + // ═══════════════════════════════════════════════════════════ + // The scan mode of the t27o workflow emits one report per spec it evaluated + // in a run; the Trinity App collects those reports through the workflow_run + // webhook and renders them as the run's report and its golden coverage + // badge. The report model lives here because this is the shape of the data + // the pipeline hands the app. What makes a report golden is decided by the + // golden criteria (specs/tools/t27o/golden.t27); this file only ever calls + // them, never restates them (#8963). + + pub const ScanReport = struct { + spec : str, + mode : str, + vectors_recorded : u32, + vectors_passed : u32, + tests_recorded : u32, + tests_passed : u32, + duplicates : u32, + sealed : bool, + attested : bool, + attestation_run : u64, + producing_run : u64, + }; + + // An empty report: the spec was listed for the run, but the scan has + // recorded nothing for it yet. + pub fn scan_report_new(spec: str, mode: str) -> ScanReport { + return ScanReport{ + .spec = spec, + .mode = mode, + .vectors_recorded = 0, + .vectors_passed = 0, + .tests_recorded = 0, + .tests_passed = 0, + .duplicates = 0, + .sealed = false, + .attested = false, + .attestation_run = 0, + .producing_run = 0, + }; + } + + // A recorded report: what the oracle measured for one spec in one run. + pub fn scan_report_recorded(spec: str, mode: str, vectors_recorded: u32, vectors_passed: u32, tests_recorded: u32, tests_passed: u32, duplicates: u32, sealed: bool, attested: bool, attestation_run: u64, producing_run: u64) -> ScanReport { + return ScanReport{ + .spec = spec, + .mode = mode, + .vectors_recorded = vectors_recorded, + .vectors_passed = vectors_passed, + .tests_recorded = tests_recorded, + .tests_passed = tests_passed, + .duplicates = duplicates, + .sealed = sealed, + .attested = attested, + .attestation_run = attestation_run, + .producing_run = producing_run, + }; + } + + // The golden verdict for one report, asked from the criteria. + pub fn scan_report_refusal(report: ScanReport) -> u32 { + return golden::refusal(report.vectors_recorded, report.vectors_passed, report.tests_recorded, report.tests_passed, report.duplicates, report.sealed, report.attested, report.attestation_run, report.producing_run); + } + + pub fn scan_report_verdict(report: ScanReport) -> str { + return golden::refusal_name(scan_report_refusal(report)); + } + + pub fn scan_report_is_golden(report: ScanReport) -> bool { + return golden::is_golden(report.vectors_recorded, report.vectors_passed, report.tests_recorded, report.tests_passed, report.duplicates, report.sealed, report.attested, report.attestation_run, report.producing_run); + } + + // A report shaped like one that meets every golden criterion. + pub fn scan_report_golden_like(spec: str, mode: str, run: u64) -> ScanReport { + return scan_report_recorded(spec, mode, 12, 12, 3, 3, 0, true, true, run, run); + } + + // A report shaped like one that has not met any criterion yet. + pub fn scan_report_unmet_like(spec: str, mode: str) -> ScanReport { + return scan_report_new(spec, mode); + } + + // How many of the evaluated specs of one run are golden. A run's ledger + // holds up to 8 reports; evaluated says how many slots carry this run's + // reports, and only those slots are read. + pub fn scan_reports_golden_count(reports: [8]ScanReport, evaluated: u32) -> u32 { + var count: u32 = 0; + var i: u32 = 0; + while (i < evaluated) { + if (scan_report_is_golden(reports[i])) { count += 1; } + i += 1; + } + return count; + } + + // The badge the app renders for the run: the floored percentage the + // criteria define -- 5 of 8 evaluated reads 62, never 63; with nothing + // evaluated the badge reads 0. + pub fn scan_reports_badge(reports: [8]ScanReport, evaluated: u32) -> u32 { + return golden::coverage_percent(scan_reports_golden_count(reports, evaluated), evaluated); + } + + test "scan_report_recorded_carries_the_oracle_evidence" { + const report = scan_report_recorded("specs/tools/mcp/trinity.t27", "golden", 12, 12, 3, 3, 0, true, true, 918273645011, 918273645011); + assert report.spec == "specs/tools/mcp/trinity.t27"; + assert report.mode == "golden"; + assert report.vectors_recorded == 12; + assert report.vectors_passed == 12; + assert report.tests_recorded == 3; + assert report.tests_passed == 3; + assert report.duplicates == 0; + assert report.sealed == true; + assert report.attested == true; + assert report.attestation_run == 918273645011; + assert report.producing_run == 918273645011; + assert scan_report_refusal(report) == golden::R_NONE; + assert scan_report_verdict(report) == "golden"; + assert scan_report_is_golden(report) == true; + } + + test "scan_report_without_evidence_refuses_as_no_evidence" { + const report = scan_report_new("specs/tools/mcp/trinity.t27", "scan"); + assert report.vectors_recorded == 0; + assert report.attested == false; + assert scan_report_refusal(report) == golden::R_NO_EVIDENCE; + assert scan_report_verdict(report) == "no recorded vector or translated test"; + assert scan_report_is_golden(report) == false; + } + + test "scan_reports_badge_is_the_floored_percentage" { + var reports: [8]ScanReport = undefined; + reports[0] = scan_report_golden_like("specs/a.t27", "scan", 1001); + reports[1] = scan_report_unmet_like("specs/b.t27", "scan"); + reports[2] = scan_report_golden_like("specs/c.t27", "scan", 1001); + reports[3] = scan_report_unmet_like("specs/d.t27", "scan"); + reports[4] = scan_report_golden_like("specs/e.t27", "scan", 1001); + reports[5] = scan_report_unmet_like("specs/f.t27", "scan"); + reports[6] = scan_report_golden_like("specs/g.t27", "scan", 1001); + reports[7] = scan_report_golden_like("specs/h.t27", "scan", 1001); + assert scan_reports_golden_count(reports, 8) == 5; + assert scan_reports_badge(reports, 8) == 62; + } + + test "scan_reports_badge_is_zero_without_reports" { + var reports: [8]ScanReport = undefined; + assert scan_reports_badge(reports, 0) == 0; + } + + test "scan_reports_badge_reads_only_this_runs_slots" { + var reports: [8]ScanReport = undefined; + reports[0] = scan_report_golden_like("specs/a.t27", "scan", 1001); + assert scan_reports_golden_count(reports, 1) == 1; + assert scan_reports_badge(reports, 1) == 100; + } + + test "scan_reports_badge_is_perfect_when_every_evaluated_spec_is_golden" { + var reports: [8]ScanReport = undefined; + var i: u32 = 0; + while (i < 8) { + reports[i] = scan_report_golden_like("specs/a.t27", "scan", 1001); + i += 1; + } + assert scan_reports_golden_count(reports, 8) == 8; + assert scan_reports_badge(reports, 8) == 100; + } + + test "scan_reports_badge_floors_when_the_division_is_not_exact" { + var reports: [8]ScanReport = undefined; + reports[0] = scan_report_golden_like("specs/a.t27", "scan", 1001); + reports[1] = scan_report_golden_like("specs/b.t27", "scan", 1001); + reports[2] = scan_report_unmet_like("specs/c.t27", "scan"); + assert scan_reports_badge(reports, 3) == 66; + assert scan_reports_badge(reports, 3) != 67; + } + From d61277762d7fd7008a4f7a686801d37dceef4006 Mon Sep 17 00:00:00 2001 From: "queen-publisher[bot]" Date: Mon, 19 Oct 2026 20:13:25 +0000 Subject: [PATCH 2/2] docs: the coordination entry this branch needs to land A pull request must add exactly one docs/now entry and a bee has no way to know that: its brief names a boundary file and acceptance criteria, and docs/now/ is neither. The publisher adds it rather than failing the gate. Closes #8997 Co-Authored-By: Claude Opus 5 --- ...b-app-install-t27o-workflow-golden-spec-prs-plu.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 docs/now/2026-10-19-published-trinity-github-app-install-t27o-workflow-golden-spec-prs-plu.md diff --git a/docs/now/2026-10-19-published-trinity-github-app-install-t27o-workflow-golden-spec-prs-plu.md b/docs/now/2026-10-19-published-trinity-github-app-install-t27o-workflow-golden-spec-prs-plu.md new file mode 100644 index 0000000000..9901848f83 --- /dev/null +++ b/docs/now/2026-10-19-published-trinity-github-app-install-t27o-workflow-golden-spec-prs-plu.md @@ -0,0 +1,11 @@ +# NOW -- Trinity GitHub App: install -> t27o workflow -> golden-spec PRs, plus the golden criteria in .t27 (published 2026-10-19) + +## A bee's work on #8997, published from `queen-8997` (Closes #8997) + +- The branch changes 2 file(s): `specs/tools/mcp/trinity.t27`, `specs/tri/pipeline/workflow.t27`. +- `git diff --stat origin/master...queen-8997` reads: 2 files changed, 237 insertions(+) +- This entry is written by the publisher, not by the bee. A pull request must + add exactly one `docs/now/` entry and a bee has no way to know that: its brief + names a boundary file and acceptance criteria, and `docs/now/` is neither. +- What this entry does NOT establish: that the work is correct. The gates on the + pull request judge that, and they are the same gates every other change meets.