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
9 changes: 8 additions & 1 deletion src/SignsOfAI.Cli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -399,8 +399,15 @@ static void PrintReport(string path, AnalysisResult r, int top, bool useColor)
}
if (r.Findings.Count > shown.Count)
Console.WriteLine(Col($" … and {r.Findings.Count - shown.Count} more (use --top {r.Findings.Count}).", 90));
// Not "reads mostly human", and not a green tick. Both were the same claim in two forms, and it
// is the one #32 removed from the report and the interface because it was never ours to make: a
// detector that detects nothing also finds nothing, and this project has deliberately never
// measured how much machine writing it catches. It survived here for months because the test
// that guards the wording reads AnalysisResult.Verdict, and this line is a literal.
if (r.Findings.Count == 0)
Console.WriteLine(Col(" ✓ No strong AI tells found — reads mostly human.", 32));
Console.WriteLine(Col(
" No strong AI tells found — nothing above the measured boundary. " +
"That is not evidence a person wrote this.", 90));
Console.WriteLine();
}

Expand Down
14 changes: 13 additions & 1 deletion src/SignsOfAI.Core/Model/Finding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,19 @@ public sealed record Finding
/// <summary>Optional evidence, e.g. "48× more frequent post-ChatGPT".</summary>
public string? Evidence { get; init; }

/// <summary>Contribution of this finding to the overall score (higher = stronger AI signal).</summary>
/// <summary>
/// How much this finding weighs (higher = stronger AI signal).
///
/// For everything outside <see cref="SignCategory.Statistical"/> this is its contribution to the
/// score: the scorer sums these into one weighted density and maps it monotonically, so more
/// weight is more score. <c>stat.burstiness</c> is the exception — the scorer excludes that
/// category from the sum and derives its share from the sentence-length distribution instead, so
/// the weight it carries ranks it against the others without being what moved the number.
///
/// This said "contribution of this finding to the overall score", which was false for that one
/// finding, and it stopped being a harmless inaccuracy the moment the evidence report began
/// sorting by it and telling the reader what the order meant.
/// </summary>
public double Weight { get; init; }

/// <summary>
Expand Down
164 changes: 123 additions & 41 deletions src/SignsOfAI.Core/Reporting/EvidenceReport.cs

Large diffs are not rendered by default.

13 changes: 11 additions & 2 deletions src/SignsOfAI.Core/Reporting/ReportMessages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ public static class ReportMessages
public const string CheckableIntro = "checkable.intro";
public const string CharactersExplanation = "characters.explanation";
public const string CharactersTableHeader = "characters.table-header";

/// <summary>
/// Why the table is not in file order. It prints line and column so the reader can find each
/// occurrence in an editor, which makes it the one table on the page they are invited to verify —
/// and a Line column reading 6, 1, 2, 3 with no explanation looks like a defect.
/// </summary>
public const string CharactersOrdered = "characters.ordered";
public const string MoreRows = "common.more-rows";
public const string CitationsIssuesNote = "citations.issues-note";
public const string CitationsNoIssuesNote = "citations.no-issues-note";
Expand Down Expand Up @@ -168,6 +175,7 @@ public static class ReportMessages
[CheckableIntro] = 0,
[CharactersExplanation] = 0,
[CharactersTableHeader] = 0,
[CharactersOrdered] = 0,
[MoreRows] = 1,
[CitationsIssuesNote] = 0,
[CitationsNoIssuesNote] = 0,
Expand Down Expand Up @@ -256,12 +264,13 @@ public static class ReportMessages
[CheckableIntro] = "These are not judgements about the writing and they did not move the score. Each is either present in the file or it is not.",
[CharactersExplanation] = "Several of these have ordinary explanations — word processors insert soft hyphens and unusual spaces on their own, and any copy-paste can carry them. Invisible characters and letters borrowed from another alphabet are harder to arrive at by accident, though pasting text can do it. This table says what is in the file, not how it got there.",
[CharactersTableHeader] = "| Character | Codepoint | Line | Column |",
[CharactersOrdered] = "Listed with the characters hardest to arrive at by accident first, then in the order they appear in the file.",
[MoreRows] = "… and {0} more.",
[CitationsIssuesNote] = "> None of this needed the internet: the document disagrees with itself. It is a question to ask, not a conclusion — the answer is usually one sentence.",
[CitationsNoIssuesNote] = "> Nothing here is a finding. It describes what could and could not be checked.",
[SignalsNone] = "None.",
[SignalsOrdered] = "Ordered by how much each one moved the score, strongest first, rather than by where it appears in the text.",
[SignalsMore] = "… and {0} more, none of which moved the score as much as any of the above.",
[SignalsOrdered] = "Ordered by how much weight each one carries, heaviest first, rather than by where it appears in the text.",
[SignalsMore] = "… and {0} more, none of them carrying more weight than what is shown above.",
[ObservationsIntro] = "Measured against writing published before generative models existed. Shown because they are real, and counted for nothing because they are ordinary.",
[ObservationsRowOne] = "- {0} — {1} occurrence",
[ObservationsRowOther] = "- {0} — {1} occurrences",
Expand Down
5 changes: 3 additions & 2 deletions src/SignsOfAI.Core/Reporting/report.en.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,14 @@
"caveat.aggregate-measured": { "text": "> **A score is not proof.** On {0} texts published before generative models existed, this build's false-positive rate at a threshold of {1}/100 was under {2} — the upper end of a 95% interval, not a guarantee, and measured on published articles rather than student work. Below that threshold, treat the score as saying nothing." },
"checkable.intro": { "text": "These are not judgements about the writing and they did not move the score. Each is either present in the file or it is not." },
"characters.explanation": { "text": "Several of these have ordinary explanations — word processors insert soft hyphens and unusual spaces on their own, and any copy-paste can carry them. Invisible characters and letters borrowed from another alphabet are harder to arrive at by accident, though pasting text can do it. This table says what is in the file, not how it got there." },
"characters.ordered": { "text": "Listed with the characters hardest to arrive at by accident first, then in the order they appear in the file." },
"characters.table-header": { "text": "| Character | Codepoint | Line | Column |" },
"common.more-rows": { "text": "… and {0} more." },
"citations.issues-note": { "text": "> None of this needed the internet: the document disagrees with itself. It is a question to ask, not a conclusion — the answer is usually one sentence." },
"citations.no-issues-note": { "text": "> Nothing here is a finding. It describes what could and could not be checked." },
"signals.none": { "text": "None." },
"signals.ordered": { "text": "Ordered by how much each one moved the score, strongest first, rather than by where it appears in the text." },
"signals.more": { "text": "… and {0} more, none of which moved the score as much as any of the above." },
"signals.ordered": { "text": "Ordered by how much weight each one carries, heaviest first, rather than by where it appears in the text." },
"signals.more": { "text": "… and {0} more, none of them carrying more weight than what is shown above." },
"observations.intro": { "text": "Measured against writing published before generative models existed. Shown because they are real, and counted for nothing because they are ordinary." },
"observations.row.one": { "text": "- {0} — {1} occurrence" },
"observations.row.other": { "text": "- {0} — {1} occurrences" },
Expand Down
12 changes: 8 additions & 4 deletions src/SignsOfAI.Core/Reporting/report.es.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,17 @@
"text": "Señales contabilizadas",
"sourceHash": "078671a3b913dc8d830dc433445ca4b4cc401debf4b6fb8829ea9376ab658cc2"
},
"characters.ordered": {
"text": "Se listan primero los caracteres más difíciles de obtener por accidente, y después en el orden en que aparecen en el fichero.",
"sourceHash": "268fa7576be22e9731efa7d6e98715ff6d62e83a1af2c40601cdc85036c3cdb7"
},
"signals.ordered": {
"text": "Ordenadas por cuánto movió cada una la puntuación, de mayor a menor, y no por el lugar que ocupan en el texto.",
"sourceHash": "6328aa4d5b8ee1e8742dad32fd9d8a92fe928d606938710e706be22d0be26f15"
"text": "Ordenadas por el peso que carga cada una, de mayor a menor, y no por el lugar que ocupan en el texto.",
"sourceHash": "5a270776bb1082eb80960b1a1b431b1a56fd4d6dd3f422544cd5959eeaf18c04"
},
"signals.more": {
"text": "… y {0} más, ninguna de las cuales movió la puntuación tanto como las de arriba.",
"sourceHash": "754e025aefdb15ea0b58eb55aecaa82c9da6799259ed0f2c64d33ccac3cfb558"
"text": "… y {0} más, ninguna con más peso que las que se muestran arriba.",
"sourceHash": "621da36ba6fb9cadbc5a4ded4368137d9bc81254ae34678cc98e0a8978aa2212"
},
"section.observations": {
"text": "Encontrado, pero a una frecuencia habitual en textos humanos",
Expand Down
4 changes: 2 additions & 2 deletions src/SignsOfAI.UI/wwwroot/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,11 @@
"rhythm.cap.uniform": "Very uniform bars — a machine-like, metronomic cadence.",
"rhythm.cap.somewhat": "Somewhat uniform — the rhythm barely varies.",
"rhythm.cap.fairly": "Fairly varied — getting closer to human cadence.",
"rhythm.cap.varied": "Nicely varied, jagged rhythm — reads human.",
"rhythm.cap.varied": "Nicely varied, jagged rhythm — the opposite of a metronomic cadence.",
"home.annotated": "Your text, annotated",
"home.annotated.hint": "Highlighted spans are AI tells — hover for the suggestion.",
"home.recommendations": "Recommendations ({0})",
"home.nofindings": "No strong AI tells found. This reads mostly human — nice work.",
"home.nofindings": "No strong AI tells found — nothing above the measured boundary. That is not evidence a person wrote this.",
"home.idle": "Start typing, paste, or upload a document — your AI-writing score appears here instantly.",
"file.toolarge": "“{0}” is too large (max 5 MB).",
"file.loaded": "Loaded “{0}” ({1} KB).",
Expand Down
4 changes: 2 additions & 2 deletions src/SignsOfAI.UI/wwwroot/i18n/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,11 @@
"rhythm.cap.uniform": "Barras muy uniformes: una cadencia de metrónomo, de máquina.",
"rhythm.cap.somewhat": "Algo uniforme: el ritmo apenas varía.",
"rhythm.cap.fairly": "Bastante variado: se acerca a la cadencia humana.",
"rhythm.cap.varied": "Ritmo bien variado e irregular: se lee humano.",
"rhythm.cap.varied": "Ritmo bien variado e irregular: lo contrario de una cadencia de metrónomo.",
"home.annotated": "Tu texto, anotado",
"home.annotated.hint": "Los fragmentos resaltados son señales de IA; pasa el cursor para ver la sugerencia.",
"home.recommendations": "Recomendaciones ({0})",
"home.nofindings": "No se encontraron señales fuertes de IA. Se lee bastante humano; buen trabajo.",
"home.nofindings": "No se encontraron señales fuertes de IA: nada por encima del umbral medido. Eso no demuestra que lo escribiera una persona.",
"home.idle": "Empieza a escribir, pega texto o sube un documento: tu puntaje de escritura IA aparece aquí al instante.",
"file.toolarge": "“{0}” es demasiado grande (máx. 5 MB).",
"file.loaded": "Se cargó “{0}” ({1} KB).",
Expand Down
Loading
Loading