feat(policy): complete Policy Pack v0.1 with enhanced schemas and CI - #2
Closed
IanFrelinger wants to merge 2 commits into
Closed
feat(policy): complete Policy Pack v0.1 with enhanced schemas and CI#2IanFrelinger wants to merge 2 commits into
IanFrelinger wants to merge 2 commits into
Conversation
- Rename appsettings.local.json to appsettings.local.example.json for security - Add appsettings.local.json to .gitignore to prevent accidental commits - Clean up any remaining tracked build artifacts
- Resolve .gitignore merge conflict - Include latest policy enhancements and CI workflow - Ensure all policy files and GitHub Actions are on PR branch
3 tasks
IanFrelinger pushed a commit
that referenced
this pull request
Aug 17, 2026
…rograms historical; document spikes/ docs/DocsIndex.md: Start Here #1 is the tester quickstart, README #2, GettingStarted #3; IntegratorGuide and consumer-template/CONSUMING.md are listed for the first time; RELEASE.md, the setup-matrix scripts and the bootstrap helpers move out of the numbered list into an "escape hatches" paragraph (RELEASE stays under Operator); a new "Trust loop / certification (experimental, hold-mode)" group makes docs/trust-loop/*, the evidence ledger, governed-pipeline, SELF-EXTEND-AUDIT, the sample objective and spikes/ reachable; "Planning & Roadmap" becomes "Planning history (historical as of 2026-08-16)" because it described a finished program. Stale status docs are date-stamped rather than deleted: - ProtocolIntegration-MCP-A2A.md said the Nexo.API wiring was "planned"; it landed in #269/#270 (Program.cs registers all four adapters and maps /api/mcp and /api/a2a/{agentId}; IngressCatalog rows McpServer/A2AAgents). - ci-pre-existing-failures.md said the Full Platform Readiness Gate was RED as of 06-21; it went green with #317-#320/#335. Kept because docs/planning/* still cite it. - GapAnalysis.md and NorthStarGapAnalysis.md predate the trust loop; both get a historical banner naming their still-open rows and pointing at the ledger, ProjectTiers and CHANGELOG. GettingStarted.md #5 contradicted DocsIndex on "installer" scripts (the scripts/install/* helpers exist but bootstrap the container lane); it now says so and points first-run readers at the quickstart. The pointer to a README section "First Successful Pipeline Run" (which does not exist) now names the sections that do. IntegratorGuide.md recommended `dotnet build Nexo.sln` (the docs guard forbids it in README); it now builds Nexo.Kernel.sln and the CLI project, with hello-brick as the smallest test example, and the compatibility matrix names the solutions that actually apply. spikes/README.md: the 27 tracked files under spikes/ were cited as PASS evidence by the ledger but undocumented. It says what each spike is, every flag of the flight runner, which ledger rows cite each spike, and that none of it is a supported entry point. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
IanFrelinger added a commit
that referenced
this pull request
Aug 24, 2026
…-003 wiring) (#390) The payoff of the signing arc. `ashlar verify` no longer prints "unsigned" when a key exists — it appends a signed entry to the instance ledger (#389) and renders CERTIFIED · signed ed25519:… · ledger #N. The trust model becomes a concrete, visible artifact. - provenance course (ProjectVerifier): joins the run only once a signed ledger exists and checks that chain via InstanceLedger.VerifyChain, fail-closed — a tampered ledger fails verification (exit 65), so `run` over a forged history is refused too. A keyless project stays at three courses and reads unsigned; zero-setup is untouched. - InstanceLedger.VerifyChain(): a synchronous form, since the verifier runs courses synchronously. The async API and its 11 kernel tests are unchanged. - VerifyCommand: async; on a passing verify with a key present, append a signed verification entry (subject = hash of the exact documents) and render CERTIFIED with the fingerprint and sequence; no key -> VERIFIED unsigned as before; corrupt key -> fail closed (exit 1); a ledger that turns corrupt between the provenance course and the append -> refused (exit 65). e2e-loop 70/70 (real binary): certifies with a key, writes a signed entry, second run adds provenance and extends to #2, tampered ledger refused at 65 — keyless verify still unsigned at three courses. Kernel 360/360 incl. new provenance tests. Adversarially reviewed (fail-closed + keyless-regression lenses); zero confirmed findings. Co-authored-by: PlzTouchGrass <130718671+PlzTouchGrass@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
IanFrelinger added a commit
that referenced
this pull request
Sep 1, 2026
Lab finding #462 (white-box), adversarially reviewed clean. The correctness gate proves EXACT witnessed behavior, but its value comparer coerced across kinds: Convert.ToInt64 rounded a double into an int (2.4 == 2, 1.5 == 2 by banker's rounding) and the string fallback equated an int with its decimal string or a bool. A brick whose real output was wrong-by-<0.5 or wrong-typed passed the proof the gate exists to make. Both comparers (WitnessValueComparer and the duplicated WitnessRunner.ValuesEqual) now match type-first: if either side is integral (resp. boolean), BOTH must be, else unequal; strings and floats keep the invariant-string compare. Same-type values still match (int 42 == long 42, "hi" == "hi", 3.14 == 3.14). New unit tests pin every closed hole and every preserved case. Also from #459: - Mutant ids are disambiguated (#2/#3 on a repeated {kind}-{line}) so the SIGNED survivor/killed ledger is unambiguous — two mutations on one line no longer share an id. - Certification record Status XML docs corrected from "ADMIT/REJECT" to the PASS/FAIL the gate actually writes (3 model files), so a consumer matching the documented labels stops misclassifying every record. Gates: cert-gate 402/402 (the comparer change broke no existing witness); certification suites 35/35. Adversarial review (Opus): clean. [coordinated-integration] Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
IanFrelinger added a commit
that referenced
this pull request
Sep 5, 2026
…he arithmetic
The mutation leg's whole job is proving the witness would notice if the brick's
logic were wrong. It could not do that for arithmetic: a brick computing
Math.Max(0, baseDamage - armor) and one computing Math.Max(0, baseDamage + armor)
-- contradictory programs -- BOTH certified "ADMIT escape_rate=0 mutants_killed=5"
against the same witness, because the catalog produced three string-literal
mutants, one int-literal mutant and one statement removal, and never touched the
operator. Reproduced before the change: both bricks ADMIT with exactly those five
mutants killed (ArithmeticMutantTeethTests failed for that reason pre-fix).
The catalog now carries the operator classes a real mutation tester has:
swap-arithmetic-op + <-> -, * <-> /, % -> *
swap-arithmetic-assign += <-> -=, *= <-> /=, %= -> *=
shift-relational-boundary < <-> <=, > <-> >= (the off-by-one class flip-binary-op cannot express)
swap-unary-op -x <-> +x, ++x <-> --x, x++ <-> x--
remove-logical-not !x -> x
Every one of them consults the semantic model before rewriting a token. The
engine scores a non-compiling mutant as KILLED, so a swap that ignored types
("a" + "b" -> "a" - "b", enum + 1 -> enum - 1, !(bool?) dropped inside an if)
would inflate the kill count of a signed certificate with mutants that were
dead on arrival -- exactly the vacuity the leg exists to prevent. The catalog
binds the candidate against the SAME reference set and the same injected usings
the certification compile uses (hoisted to a sibling tree as global usings so
candidate line numbers and ToSource() are untouched); the engine now passes its
compilation references through. An operand whose type cannot be resolved yields
NO operator mutant rather than a guess. Whole-expression constants, constant-zero
divisors and steps of loop-control variables (for/while/do conditions) are
skipped: the first two are compile errors after a swap, the last is a mutant that
never returns and would hang the in-process harness instead of being judged.
OperatorMutationCatalogTests compiles every operator mutant of five fixtures,
including an operator zoo covering ints, doubles, decimals, int?, char, enum,
string, DateTime/TimeSpan, bool and bool?.
Ids stay {kind}-{line} with the existing #2/#3 disambiguation, and the five
pre-existing ids for the reproducing brick are pinned verbatim so records
signed before this change stay reproducible. Strategy names name every kind
(a test now checks the engine's list against what the catalog emits -- it caught
a missed edit during this work).
Consequence handled here, not handed off: the boundary operator exposed that
every "strong" probe-brick witness in the suite was toothless at the zero-error
boundary -- errorCount > 0 mutated to >= 0 survived because no witness ever fed
a log without an ERROR line. Those tests went red; that is the gate doing its
job. The witnesses were STRENGTHENED (MutationProbeWitnesses.ZeroErrorCase, one
shared case across ten sites), not the mutant weakened. All suites green.
Timing on the simple bricks (dotnet test durations, same machine): the probe
brick's strong-witness certification 3 s before and 3 s after (+1 mutant); the
line-substring-counter engine run 2-3 s before and 2 s after (+2 mutants); the
reproducing brick 5 -> 6 mutants. The binding-only compilation adds tens of
milliseconds per certification; the leg is bounded by the per-kind cap of 4,
so worst case grows from 28 to 48 mutants on a brick that has every shape.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>IanFrelinger added a commit
that referenced
this pull request
Sep 5, 2026
…ls the witness never earned
Two adjudicated HIGH findings against AstMutationCatalog, each reproduced by a pair of
contradictory bricks that certified TOGETHER against one witness:
- MaxPerKind = 4 stopped every operator kind after its first four qualifying sites in document
order. A shipping brick with six arithmetic sites never had `+ surcharge - discount` mutated, so
cap-minus and cap-plus (differing only in that operator) both certified escape_rate=0 against a
witness with surcharge=0, discount=0, and the record carried no trace of the truncation. The cap
is gone - none per kind, none per site. A brick is one file and a mutant costs a fraction of a
second; a cap that has to be surfaced in the record to keep the record honest is not worth it.
- IsExecutionMethod mutated ExecuteAsync and private INSTANCE methods only. Arithmetic in a
private static, internal or public helper - or a property body, a constructor, a nested type -
was never mutated, so static-minus/static-plus/internal-minus all certified with every kill owed
to input keys and a non-compiling statement removal. Scope is now every member body of every
type in the candidate (methods, constructors and their initializers, accessors, expression-bodied
members, property and field initializers; local functions and lambdas ride along), whatever the
modifiers. The loop-control guard stays.
The skeptics also flagged two kinds of kill the witness never earned; both are removed at the
catalog (the engine is untouched):
- Non-compiling mutants are discarded, not emitted. The engine scores them as killed, but that
kill is the compiler's. Each mutant is re-bound in the same binding compilation the operator
family already uses (candidate parsed alone, the wrapper's usings hoisted as global usings, the
engine's reference set). When the candidate itself does not bind, nothing can be judged and
nothing is discarded.
- Lookup-key literals are not mutated: the index of an element access that is read, and an
argument that binds to a NON-VOID method's parameter named `key` (BrickInput.Get, TryGetValue,
ContainsKey, GetValueOrDefault). A mutated lookup key fails on every input whatever the witness
expects. Store keys (BrickOutput.Set, dict[k] = v) stay mutable: dropping a declared output is
observable only by a witness that asserts it. An invocation that cannot be bound falls back to
a short list of lookup member names so a blind run behaves the same on the common shapes.
Widening the scope exposed equivalent mutants on honest bricks; handled at the operator:
- Identity operands are not swapped (x * 1 <-> x / 1, x + 0 <-> x - 0, and the compound forms).
- A constructor statement assigning a field or property NOTHING in the candidate reads (Id, Name,
Version, Description, Interface = new BrickInterface { ... }) is out of scope: no member computes
anything from it, so no witness case could kill a mutant of it. This is a data-flow rule, not a
name list: a member the constructor writes and any body reads (`_rate = 5`, multiplied in
ExecuteAsync) stays in. Without it every literal of every brick's metadata block survived.
- The probe fixtures' ExtractErrorMessage carried an unreachable guard (`if (idx < 0)` on lines
the caller had already filtered by "ERROR") and a TrimStart that absorbed an off-by-one in
`idx + 5`: three equivalent mutants the moment helpers were in scope. Rewritten as one reachable
expression in all four fixtures (compiled and source, probe and nondeterministic), so every
mutant of the helper is observable and StrongWitness still admits.
Samples, certified with the export tool built from this tree, 0 survivors each: DamageResolver 6
mutants (was 10: the four input-key kills and the non-compiling remove-statement-42 are gone; the
output-key mutant mutate-string-literal-53, which the cap had cut off, is in), HelloBrick 2 (its
two output keys; the input key and the non-compiling removal are gone), and the brick template
scaffold (BrickCertificationProjectLoaderReferenceTests). The tracked DamageResolver record is
regenerated through the export tool; contentHash unchanged, only mutants, timestamp and signature
moved.
The adversarial oracles, run through that tool: cap-minus and cap-plus both REJECT at mutation
(escape 0.25; swap-arithmetic-op-40 and #2 named with the `- discount` edit); static-minus,
static-plus and internal-minus all REJECT at mutation (escape 0.67; swap-arithmetic-op-37 named).
All five ADMITTED before this change.
Tests: MutationCoverageTeethTests (new), two VacuousMutationLegTests facts (kills must be owed to
an expectation; a witness that adds nothing over the vacuous one is toothless and rejected), and
the OperatorMutationCatalogTests id pin now asserts the input-key and remove-statement ids are
GONE rather than present. 23 of them fail at 8a6e862 and pass here. Focused 38/38; the
regression filter 112/112; nine further certification classes 56/56; conventions 7/7.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Complete Policy Pack v0.1 implementation with:
Includes comprehensive safety rules, quality gates, and automated CI checks.