Skip to content

The clampdown: test-write gate wired live, test_quality v5 pack in shadow, json_handler contract suite, test inventory phase A (DPLAN-0323, FPLAN-0468/0469) - no deletions, v4 untouched - #751

Open
AIOSAI wants to merge 28 commits into
mainfrom
dev

Conversation

@AIOSAI

@AIOSAI AIOSAI commented Sep 2, 2026

Copy link
Copy Markdown
Owner

No description provided.

AIOSAI and others added 16 commits September 1, 2026 17:54
…lt and proven (unwired), test-inventory phase A lands with its gitignore rule (DPLAN-0323, FPLAN-0468/0469). HOOKS: testwrite_gate.py (29th handler) blocks agent creation of new test files behind .aipass/test_write_policy.json - Patrick's switch ruling: off now, allow-list for canary, field flip to re-enable, block_test_edits shipped false but LIVE with its own pin. Fail-closed on missing AND corrupt policy - hooks' own ruling with the reasoning on record: bash_writes' allow-and-log does not transfer because here the file IS the policy question, and a ruling a stray rm silently repeals is not a ruling. Safe because both lockout-preventing properties are PINNED not prose: non-test writes never read the policy, and writing the policy file is itself never a test write. Admin seat checked before the policy read; crash inside the gate allows (fail-closed covers unreadable policy, not the gate's own defects). One deviation accepted: *_test.py matched too - pytest collects it, so the agreed shape was escapable by rename. The 5-leg admin rail EXTRACTED to modules/admin_seat.py - same code not a copy - and the extraction bit exactly as red-first deserves: a second import that can fail reached edit_gate's crash-guard-allows path, meaning a missing admin_seat.py would have exempted every seat; both gates now refuse on unimportable rail, both halves pinned. Evidence: 1850x2 green, 54 new pins, audit 100 with baseline measured clean first, 13/13 designed mutants killed, one first-round survivor promoted to contract pin (writing_enabled was decorative for module-API callers - gate reads .error first), live-proven through real engine.dispatch: tool-lane block, scripted-lane block, edit-of-existing allow, ordinary source allow. NOT WIRED on purpose: hooks.json is hash-enrolled and every edit darks all hooks until aipass trust re-runs - a deliberate human checkpoint, held for Patrick. Policy file itself is gitignored by .aipass deny-all - ships nowhere; the exception line is a developer-permission decision, held for Patrick with the recommendation to ship it. SEEDGO: FPLAN-0468 phase A - test_inventory handlers + inventory module + drone verb, static ranked pass over ~19k fleet tests, commit unblocked by seedgo's own .gitignore family rule (.seedgo/test_inventory*) so the 28MB rows artifact stays local forever. HELD OFF THIS TRAIN deliberately: seedgo's pytest_quality_standards pack (phase 1 complete, proven, 2967x2 green) rides the phase-2 train because phase 2 is actively writing into that directory right now and a dir mid-write does not get committed. Campaign context for the log: test_quality v4 graded by substring presence over raw text and CI gated the average at 100 - all 18 branches were forced to carry all 99 pattern strings; the v5 pack judges what a test proves, in shadow mode, and v4 stays untouched until v5 is proven.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…test-write gate awareness (DPLAN-0323, Patrick's 1-3 GO + awareness ruling 18:03). THE AWARENESS HALF, his words as the reason: we can't just block and expect the fleet to magically know - test creations require permissions. Tier 1 navmap house rules now carry it: creating NEW test files requires permission, the gate refuses by policy, editing existing tests stays fine, a new test goes through @devpulse with the defect or contract it pins, and a refusal names the policy and the cure - so every citizen sees the rule on cadence before ever hitting the gate. THE SHIPPING HALF: !test_write_policy.json exception added to .aipass/.gitignore under Patrick's explicit developer permission (the file's own comment reserves that call), with the reasoning inline - a fresh clone must receive the ruling and its note, not fail-closed-by-accident with nothing to diff. The policy file itself now ships: off, allow-list empty, block_test_edits false, the note naming who ruled, when, why, and both cures. Remaining from the decision block: the hooks.json wire (deliberately held - darks every hook until aipass trust re-runs; scheduled with Patrick after phase 2 lands) and the project_hooks.json template entry (dispatched to @hooks, rides their train).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s teach it (DPLAN-0323 awareness, Patrick 18:03). project_hooks.json template now stamps testwrite_gate into every aipass init project - entry after rm_gate, matcher covering both lanes, and PINNED where template drift is invisible to fixtures: three live-config pins (entry present, handler real+enabled, matcher keeps Bash - losing it halves the gate), placed deliberately in test_live_config_timeouts.py where position-dependent live reads are already declared. registry_gate DECLINED for the template with the reasoning on record: it matches filename shape alone and its refusal hardcodes spawn advice that is wrong for an external project's own FOO_REGISTRY.json - the failure mode that teaches people to route around gates; it wants its own measurement, not a ride-along. Awareness wired into BOTH refusal messages: the creation refusal now carries the clause that IS the ask (a new test goes through @devpulse NAMING the defect or contract it pins - recipient without clause = re-ask, not review), and the missing-policy refusal - the message most fleet projects will actually meet, since init stamps the gate but no policy - now names Patrick, the date, DPLAN-0323, and that editing existing tests is untouched, so fail-closed reads as a fleet ruling instead of a broken install. Evidence: 1855x2 green, audit 100, 5/5 new mutants killed (clause dropped, pointer dropped, ruling dropped, entry removed, matcher loses Bash), both refusals read end-to-end live including a fresh no-.aipass tmp project. No trust re-enroll needed - enrollment hashes hooks.json only, verified in trust_registry.py before touching the template. FLAGGED for @aipass, not acted: init could stamp a default policy so fresh projects land configured rather than on the missing path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…coring the fleet in 70s, shadow mode, v4 untouched (DPLAN-0323 phases 1+2, FPLAN-0469). THE PACK: stdlib-only, liftable onto any Python project, auto-discovered under the pytest_quality key with the tests lane word deliberately unclaimed. Eleven checks, each check+content+md: no_oracle, assertion_shape, unentered_assert, capture_never_read, empty_parametrize, mock_drift, self_skip, posix_literal, entry_point_diff, coverage_slot, docstring_pin - plus a pack README that IS the v5 standard document, carrying the classify-and-return disposition natively so the silent_catch bypass dies when the standard learns it. ONE PORT REFUSED on the record: ruff_pt shells to a binary and imports the framework logger - an execution check wearing a nominator's filename; porting it would have ended the stdlib-only property for one rule CI already runs. Ten ports plus docstring_pin, an accurate count over a full-looking one. DOCSTRING_PIN built STRUCTURAL as ruled - the docstring must name an importable symbol the test ACTUALLY CALLS, no word list; the adversarial pin is the reason it exists (identical pin-stuffed prose passes only when a called symbol is named). Ships SCORED=false publishing the full list while reporting 100, because the first real measurement proved the unscored fallback right: 16,857 of 18,780 fleet units (89.8pc) name no symbol they call - gating that on day one fails all 18 branches, which is exactly how a standard teaches gaming. FLEET SHADOW REPORT: excluding docstring_pin, 1,369 flags across 18,780 units - mock_drift 468, assertion_shape 295, no_oracle 203, capture_never_read 132, unentered_assert 110, self_skip 99, the rest under 40. Outliers named: commons 80/api 91 mock_drift, entry_point_diff not_applicable on 13 of 18 (the honest path), and seedgo 50 on its own two brand-new inventory verbs named by no test - the tool convicting its author, third time this campaign. FOUR REAL BUGS in the shared readers, all red-first pinned, each reverted to prove the pin fails alone: the inherited or-fallback in tests_pytest_standards corpus.py CURED as its own scoped item (src/tests projects collected 0 files before, 2 units after) plus a SECOND inherited defect in the same file - SKIP_DIRS pruned against absolute path.parts, so any target LIVING under a dir named .venv or node_modules had every test skipped (0 units before, 1 after; matters most for the audit-tests lane which copies targets into temp trees it does not control; lane suite 479 green after). Same absolute-path bug found copied into the new pack's own corpus, plus production_trees admitting test-shaped files outside test_dirs - a ghost verb readable as an unexercised production entry point, reproduced exactly then fixed so production excludes every test-SHAPED file. UNREACHABLE ARMS in the original nominators named not carried: patch.object can never nominate, a double-walked decorator pass, coverage_slot's whitespace-needing class arm, unentered_assert's floor behind its own exemption - dropped where safe with reasons in docstrings, still present in the execution pack, banked not swept. Evidence: 3141x2 green (174 new pins, every one mutation-verified), checklist 34/34 on every new py, audit 100 all 45 standards, two hardcoded /tmp fixed rather than bypassed. Phases 3-4 (teaching templates, contract suite) ride the next train.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… rules, shadow numbers, docstring_pin unscored at 89.8pc fleet miss rate, v4 stays the gate until the weekly cycle proves v5.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…FPLAN-0469). Three worked examples in pytest_quality_standards/templates/ - seam_test.py, failure_path_test.py, contract_test.py - each runnable and self-contained, each showing the WRONG version FIRST with the reasoning inline, each carrying a DO-NOT-STAMP block citing the measurement not an opinion (1,411 tests in five stamped families; of 48 names spanning 6+ branches only FIVE still share a shape). The wrong versions are proven wrong the honest way: seam's tautology (mock a value, assert it back - 7==7 with a stand-in in the middle) stays GREEN against a caller that never touches the ledger while the right version raises; failure_path teaches BOTH wrong shapes (bare raises stays green against wrong-type-no-message; a 7-input whitespace matrix all reaching one branch stays green against a vanished audit line) and pairs every refusal with a success test because a subject that raises unconditionally passes every failure test ever written; contract's wrong version computes its expectation with the implementation's own sort key and its second wrong version pins a private helper that dies on an honest refactor. Wrong versions deliberately not named test_* so nothing collects them - but they are EXECUTED by the collected proof tests, so no wrong shape is dead code. The templates PASS THE PACK THEY SHIP WITH: all eleven rules 100 or honest not_applicable, including docstring_pin's measured 100 - not its unscored fallback. Pinned OUT OF BAND against rot: a subprocess-pytest test runs all three with default patterns restored, and a second pin holds the other arm (must NOT be collectible by the branch pattern) so nobody later cures the first pin by renaming templates into the fleet's green count; proven red-first. A bypass ADDED THEN REMOVED on the record: dead_code exclusions live in .seedgoignore not file-scoped bypass, so the entry never fired - and a bypass matching nothing reads as a decision that was never made; the .seedgoignore entry carries the full reasoning instead. The ninth pyright error became the better template: the seam collaborator is now a Protocol (SupportsAppend) - the seam written down, naming the one call that crosses in the signature rather than only in the mock. Spawn spec MAILED not stamped: their citizen templates teach the permission path (new test = mail @devpulse naming the defect or contract it pins; a request without the clause is a re-ask), with a link to the DO-NOT-STAMP block rather than copies of the files - copying being the exact mistake the files document. Evidence: templates 12 green under default patterns, suite 3143x2 (up 2), audit 100 all 45, checklist 34/34, ruff clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…LAN-0323, FPLAN-0469 - build phases complete). THE HEADLINE IS A NUMBER THAT SAYS NO: a filename-keyed merge of the five stamped families would destroy 1,363 of 1,411 tests and keep 48 - three of the five families have ZERO consolidatable tests, so phase 7 walks the CANDIDATE list, never the family list. Shape-gated harvest confirms the turn-1 refutation exactly: 140 twin identities at 2+ branches (468 tests), 9 identities at 6+ covering 67 tests - the honest number. The widest candidate (test_conftest_fixtures_available, 17 branches) lives OUTSIDE all five families in test_scaffold.py; the fight was never where the prize was. THE SUITE: test_json_handler_contract.py, 216 (impl,contract) pairs discovered by glob so a new branch enrolls automatically - 179 passed / 31 skipped / 10 xfailed, every skip and xfail NAMING the branch and divergence, because a green row asserting nothing is the exact defect this campaign exists to kill. NINE REAL DIVERGENCES published, NONE fixed (not seedgo's trees, not their call): the sharpest is save_json into an absent directory splitting THREE ways - 9 branches create it, 5 return False writing nothing, 4 raise uncaught FileNotFoundError - no call site can be correct against all three and two of three LOSE THE DOCUMENT on a fresh checkout's first path. Same 12/5 split on invalid payloads makes if-not-save_json dead code on 12 branches and the only guard on 5. commons returns str where 16 return Path; load_json is not a pure read (17 handlers CREATE the file); save_json mutates the caller's dict on all 17. validate_json_structure is the ONE genuine shared contract - all 16 answer a 10-case matrix identically - pinned as such. A CORRECTION ON THE RECORD: turn-1's canary/memory/spawn-expose-nothing claim was AST-only reading and wrong - at runtime all 18 bind the full surface, verified directly rather than trusting the sub-agent. THE TOOL CONVICTED ITS AUTHOR, FOURTH TIME: dead_code dropped seedgo to 99 because twins.py was unreferenced - the honest cure was wiring it to the CLI (a report nobody can run IS dead code), and the wiring exposed worse: the first live run printed 0-twins-over-0-branches as GREEN SUCCESS because the fleet target resolved one level off; branch_dirs now REFUSES to publish a zero and derives the container from the registry. Six mutants killed across both fixes. IMPORT SIDE EFFECTS found auditing the suite's own syscalls: 4 of 18 handlers write into the live repo on import, plus a cross-branch write channel (any branch's logger.warning reaches trigger's json_handler and writes a third branch's state) - closed by quarantining every discovered handler's directory, one residual named in the fixture docstring with its full call chain rather than silenced. Corpus scope split stated not smoothed: 18,556 in-branch vs ~19,437 with skills/lib + spawn templates - and the stamp SOURCE (spawn/templates/citizen/tests/test_json_handler.py) sits outside every measured population, so the residue is measured against copies whose original is invisible. Evidence: 3346x2 green both rootdirs (up 205), checklist 34/34 and 23/23 every touched file, ruff clean, pyright 0, audit back to 100 including dead_code and trinity (their own checker failed their own memory entries mid-close - trimmed, not bypassed). Devpulse verified before commit: 227/31/10 re-run green from the devpulse seat in 3s. Phase 7 now has its candidate list and, more importantly, its residue list - the thing that says what NOT to touch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…he contract suite - the refusal number (1,363 of 1,411 not consolidatable by filename) and the three-way save_json split on the record.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n (DPLAN-0323, Patrick's word 21:18). Entry after rm_gate, matcher Bash|Edit|MultiEdit|Write|NotebookEdit, aipass trust re-enrolled in the same breath so the dark window lasted seconds. PROVEN LIVE POST-WIRE through the real chain, not assumed: git_gate still fires (hooks un-darked), a real scripted test-file creation from the admin seat was detected, seat-checked, and ALLOWED with the log line naming @devpulse - by design, the seat is the one door left open. Four probes through the exact bridge Claude Code invokes (bridges/claude.py PreToolUse, fabricated non-admin cwd at seedgo): NEW test via Write tool DENIED exit 2 with the full teaching refusal (Patrick's ruling+date+DPLAN, both cures, the navmap clause, the mail command); NEW test via scripted Bash heredoc DENIED on the scripted lane; EDIT of an existing test ALLOWED silently; ordinary source Write ALLOWED with the policy never read. Gate suite 56 pins + 6 live-config pins = 62 green in 1.2s. Probe artifact removed via drone rm. The fleet now meets the refusal, and the refusal teaches the cure.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…the one documented off-switch for Claude Code 2.1.258's background daemon (DPLAN-0324, Patrick's word 23:25, manual BAUD test passed 23:42/23:53). THE INCIDENT: three devpulse sessions alive at once, resumable from the phone, while Ctrl-C x2 could not restart the seat. Not adoption - a human hit option 2 (Move to background and exit) at the new exit dialog once, and from then on BAUD's -c door SKIPPED the live background job (2.1.257 no-longer-reopens-a-stalled-copy) and landed on the next transcript down, resurrecting a stopped Aug-31 fork four times in one night. THE FIX IS A RESTORE, NOT A REDESIGN (Patrick: its not a ruling, its how baud worked - rewire for the exact same results): disableAgentView / CLAUDE_CODE_DISABLE_AGENT_VIEW turns off claude agents, --bg, /background, the on-demand supervisor AND removes option 2 from the dialog = 2.1.228 behaviour back with BAUD, session_boot and presence_gate untouched. Cost: /fork old behaviour and /subtask, neither used here. Shipped through the manifest env block because that is the channel aipass doctor renders and checks against ~/.claude/settings.json (doctor: 35 pass, 0 errors once user settings carried it). MEASURED, not assumed, with throwaway probes and no pytest (test-session work is incomplete by Patrick's rule): user-level settings gate every subcommand (agents/stop/attach/daemon/respawn/--bg all answer disabled); project-level .claude/settings.json does NOT gate the CLI, so that edit was reverted; settings env 1 beats a shell =0, so claude stop is unreachable once the switch is on - the switch was lifted for one minute so the manual sequence (claude stop 98c9785a, then BAUD resume) could run, then re-applied; a SIGTERMed bg job is NOT respawned by the daemon (hooks' cc_sessions.py:317 docstring says it is - unsourced, on the hardening list); drone @hooks sessions reclaim refuses bg kinds, so my first sequence would have stopped nothing - caught by the pointer-graph report before Patrick ran it. RESULT: one devpulse, interactive in tmux, Fable 5.1, zero bg jobs, daemon and warm spares gone, Ctrl-C x2 exits clean, BAUD agent-card resume lands in the same chat. Hardening (plan only, owners mailed next): presence_gate bg exemption (ruling a, DPLAN-0310) now rests on a false premise; session_boot daemon-stop paths dead code; manifest pinned_version 2.1.228 vs running 2.1.258; phone and desktop doors disagree for a bg seat (pointer_graph.md sec 5.3); who may hold the pointer pen for managers is open in the record (sec 6).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hw2gQBYHFHLnyYPCT7UC8C
…admin seat can now mail and dispatch any citizen in any directory (Patrick's ruling 2026-09-02 00:23). THE RULING, verbatim: exclusion is not the plan. u can dispatch any agent any directory no restriction thats what the admin is, and change any file, except .secrets and any sudo. THE GAP: wake.resolve_branch has carried the external tier since FPLAN-0460 phase 5 (2026-08-30, built after @vera's first supervised fire failed) - but deliver_email_to_branch built its address map from the AIPass registry + the caller's project registry + the admin projects/* sweep and stopped there, so a dispatch to @vera (Vera-Studio, a sibling repo) was refused at DELIVERY with 'Unknown branch email: @vera (available: 22 branches)' before the wake, which already knew the address, ever ran. Discovery and delivery read different definitions - the exact failure the gateway was built to end. THE FIX: registry/read.py gains get_external_branches(repo_root) - one call into @memory's fleet gateway (fleet.external_branches), never a second reader of AIPASS_ROOTS.json, contained to an empty map + warning on failure; delivery.py adds it as the LAST tier after the projects/* sweep, ungated exactly like the wake tier (declaration IS the credential) - the cross-project boundary check downstream still refuses an unverified sender, so this widens discovery, not policy. PROVEN LIVE, no pytest (test-session work incomplete by Patrick's rule): drone @ai_mail dispatch @vera from the devpulse seat -> mail delivered, pointer resume 59ab2b8c, dispatch lock, registered 5ed35b24, systemd scope dispatch-vera alive, wake-back armed. ruff clean, pyright 0 on both files. ALSO ON THE RECORD: my passport said 'dispatches ANY agent' while DPLAN-0288's build note said Vera-Studio 'out of scope (phase 2)'; I read the build note as the design and told Patrick the lane was deferred. Passport restated with the ruling; compass entry added. The pin test for this defect waits for the test-session work to resume (todo 203).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hw2gQBYHFHLnyYPCT7UC8C
…on as the full dispatch verb (dispatch.py 3.1.0 -> 3.2.0, @ai_mail's fix from the trace devpulse asked for). FOUND tonight from the admin seat: drone @ai_mail dispatch @vera resolved, took the lock and spawned, while drone @ai_mail dispatch wake @vera answered 'manager - wake skipped, caller must mail'. Same seat, two verbs, two answers. @ai_mail's trace: the manager gate was working both times - it has three bypasses (scheduled, ADMIN, sender daemon) and the full verb passed admin=is_admin after the 5-leg check, but _orchestrate_wake called wake_branch with no admin argument at all, so the grant was dropped before the gate ever saw it. One lane never asked the question. Fixed: _orchestrate_wake runs the identical 5-leg block and passes admin=is_admin; non-admin behaviour byte-identical (verified from ai_mail's own seat where resolve_verified_caller is empty). The admin=True half is NOT yet proven live - it needs a manager target from the devpulse seat and Patrick ruled Vera left alone tonight; proven on the next legitimate manager dispatch. Owner's evidence: 1446 passed / 0 failed both rootdirs, seedgo audit 100, checklist 34/34, ruff clean, pyright 0. ALSO FROM THE SAME TRACE, on the record and NOT changed here (Patrick's call): the external-tier delivery fix (0748d1b) retired the outer fail-closed layer for external recipients; the surviving _check_cross_project_boundary (delivery.py:379-386) returns ALLOW when AIPASS_CALLER_CWD is empty or resolves to no project root - measured: caller cwd set -> refused, unset -> allowed. No live incident; reachable by drone's deliberate omit-when-no-cwd and by @trigger's in-process sends. Recommended cure: fail CLOSED at the boundary check for a recipient that resolved through the external tier. Two pins owed when test writing resumes: same manager-gate verdict from both verbs; unverified sender with no caller cwd must not reach a declared external (red today).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hw2gQBYHFHLnyYPCT7UC8C
…gistry - PR #751 board red since the phase 4 commit, one fix (FPLAN-0474, DPLAN-0323 residue). THE RED: test_the_fleet_target_walks_the_branch_container_not_the_repo_root failed on every board since d83aadc (01a2a79, b22af96, 92d0e97), all four Python legs plus coverage, one test, same assertion: container == repo root. THE MECHANISM (devpulse reproduced it in a tracked-only extract of HEAD before dispatching): inventory._branch_container took the common parent of the registry's branch paths; AIPASS_REGISTRY.json is machine-local and gitignored (.gitignore line 26), so on any fresh checkout discovery answered nothing, the parents set was empty, and the function fell back to root.path, the repo root - publishing '0 twins over 0 branches' as a success, the exact defect the pin was written for. Green on developer machines only because the registry exists there. A fallback path broader than the happy path; same species as the watchdog storage resolver red in FPLAN-0461 round 11. THE CURE (seedgo, owner): the container is read off the imported aipass package's own directory (__file__, then __path__ for a namespace package); if neither answers it raises naming the cause rather than guessing the repo root. The repo-root fallback is gone, not widened. Same technique the phase-4 contract suite already uses for discovery. THE PINS: the existing pin stays; one method added in the same class (no new test file - the gate is live) asserting the registry-less world via an empty branch map, mutation-verified against the restored pre-fix code: the old pin PASSES on broken code locally (the registry exists here, so it could only ever fail on CI), the new pin FAILS. Owner's evidence: 3347 passed / 35 skipped / 10 xfailed both rootdirs; seedgo audit 100 average, 47 standards, 0 violations; checklist 34/34 inventory.py + 23/23 test file; ruff clean; pyright 0. Devpulse's evidence: 28/28 in the registry-less tracked-only extract with the cure copied in. v4 untouched, nothing deleted, nothing else on this PR. Banked by seedgo as a candidate pack checker: a resolver whose fallback branch is broader than its happy path is statically detectable.
…silently (DPLAN-0323 phase 6, seedgo's demand from the plan review). EXPECTED_STANDARDS = 47 pinned by hand in the CI audit script: 46 *_check.py in the aipass pack + the diagnostics checker. Every branch's scored-standards count must equal it or the job fails naming the count and the scored list; before this, retiring a checker (or breaking its import) would have quietly averaged one fewer standard, still printed 100, and left the board green - exactly how v4 could have left the gate unnoticed once v5 replaces it. The number moves ONLY by hand, in the same commit that adds or retires a standard. Measured before pinning, not assumed: the script run locally scores every branch at exactly 47 (the plan had guessed 45; saved audits show 45/46/47 across dates as standards were added). Also ruff-formatted one pre-existing block in the script. The seedgo checklist flags on this file (3-layer architecture, bare print, META block, constant naming) are pre-existing and not applicable - it is a CI helper whose print() IS the Actions log, not a branch module.
…ses off, PR comment off (DPLAN-0323 phase 6, Patrick's ruling 2026-09-01: 'I don't think our framework is built that way'). The coverage CI job keeps running and uploading, so the codecov dashboard stays available as an instrument; nothing gates or comments on a PR any more. Phase 0 verification had shown the patch status painting a red X on PR #750 even though codecov/patch PASSED (79.31 vs a 50 target - the glyph meant lines missing, not failure), which incentivised coverage-chasing tests: the bloat the campaign exists to kill. Codecov was never a required check (confirmed against main's required contexts; fail_ci_if_error false), so no gate changes hands. Config validated against codecov.io/validate before commit.
…he repo venv - mutation sampling is the test-quality pyramid's ground truth (DPLAN-0323 phase 6). A surviving mutant means the test lied about what it pins; the pyramid is static -> haiku bulk -> sonnet/opus contested -> mutation sampling as truth (calibrated 29/30 in FPLAN-0468). Measured tonight: mutmut 3.7 refuses to run from a branch directory without a [tool.mutmut] block (FileNotFoundError, could not figure out where the code to mutate is) and guesses src/ from the repo root, so it runs from the root and the sampling instrument that drives it is seedgo's - config question sent to them under FPLAN-0476, pyproject stays devpulse's to change. CHANGELOG: the clampdown section gains the FPLAN-0474 cure under Fixed and the three phase 6 commits under Changed, with the deliberate non-wiring of the v5 pack stated (inert at CI and the checklist until the shadow cycle ends).
AIOSAI and others added 12 commits September 2, 2026 02:22
…t board caught the trinity standard standing down on every clean checkout (DPLAN-0323 phase 6, FPLAN-0476). THE CATCH: board 5effd51 failed seedgo-audit with 'ai_mail scored 46 standards, expected 47'. Not a defect in the pack: trinity_check returns not_applicable on a clean checkout (its own documented arm - .trinity/ is machine-local and gitignored, line 28 of the root .gitignore), and branch_audit keeps a not_applicable standard in results[] but out of scores[] by design (a 0 would blame the branch for an environment fact, a 100 would claim a measurement that never happened). So CI has never scored trinity: 46 gate on CI, 47 on a developer machine - which is why ai_mail's trinity fail seen locally tonight (local.json numbering, their untracked file) could never have reached a board. THE CURE: consulted = scored + not_applicable; the tripwire fires only when a standard VANISHES, and the per-branch line now prints which standards stood down and why the count still reads 47. A crashed checker scores 0 and stays counted. PROVEN before push, both worlds: the script run from a tracked-only git-archive extract (no .trinity anywhere) = 18/18 branches at 100, 46 scored + not applicable: trinity, exit 0; run locally = 47 scored per branch. Other legs on 5effd51 all green (lint, 4 test legs, coverage, e2e-wheel x3, setups, codeql, dependency-scan) - the codecov and mutmut commits stand. ruff format + check clean; the seedgo checklist flags on this CI helper remain pre-existing and not applicable.
…-vs-haiku shadow diff, and the mutmut config answer (DPLAN-0323 phase 6, FPLAN-0476). THE VERB: drone @seedgo shadow-cycle run (--no-mail for Patrick's own runs) - three measurement passes in one: the v5 pytest_quality shadow score over 18 branches (scoring, gating nothing), the ranked test inventory, and the twins report; mails @devpulse one screen of headline counts plus artifact paths, and prints the same rendered block so terminal and inbox cannot disagree. Reuses inventory._fleet_container() - the registry is never read, so tonight's CI red cannot recur here. Live run: fleet average 98, 19709 test functions in 628 files (19300 run, 409 never do), 9 consolidation candidates / 67 tests, residue 1363 of 1411; the mail arrived. THE SCHEDULE: .daemon/schedule.json, interval 10080 minutes (the daemon has no weekly type), sonnet fresh wake, prompt scoped to run-report-stop. LANDS enabled:false ON PURPOSE - an interval job with no runstate entry fires on the next tick and locks its weekly rhythm to that arbitrary hour; the Sunday 03:00 slot can only be set by seeding last_run in @daemon's runstate, a cross-branch write seedgo refused to make. Switch-on = seed first, enable second, written into the job's _note. New artifact family .seedgo/shadow_cycle* gitignored in the same change with the reasoning inline. THE DIFF (docs/v5_vs_haiku_shadow_diff.md): population-level - the haiku per-row verdicts were never written to disk (searched .seedgo, plan archives, repo, @memory twice; the deletion dossier found the same independently), and seedgo refused to re-run a classification to manufacture a substitute. On the one overlapping rule v5 does not convict what haiku cleared: 305 vs 308 of 526, by the same two mechanisms (263 delegating helpers + 45 mock asserts). False-conviction pressure sits 100 percent in docstring_pin (264 of 308 healthy rows), the rule already shipped unscored; the other ten rules land zero. Against gating yet, on the record: TEST_DIRS is top-level only, so devpulse/tools/hook_engine_poc and skills/lib/telegram/tests are invisible to v5 - and that blind spot holds 5 of haiku's 7 NO_ORACLE rows; 66 of the 263 delegating clears delegate to a production symbol rather than a checking helper (upper bound on over-clear); 995 of the 1198 non-docstring flags have never been judged by anything. MUTMUT: nothing added to the repo-root pyproject - mutmut 3.7 has no CLI flags for source paths or runner and reads pyproject.toml from CWD, so a root block would make a bare mutmut run look valid while mutating all 18 branches against the full suite; the sampling instrument will materialise a scratch copy per branch with a disposable [tool.mutmut] block and sample by MUTANT_NAMES (verified on a throwaway package, 1/1 mutant killed). Deprecated keys noted: paths_to_mutate/tests_dir -> source_paths/pytest_add_cli_args_test_selection. TWO DEFECTS FOUND IN SEEDGO'S OWN AUDIT VERB, reported not fixed: audit pytest_quality overwrites .seedgo/last_audit.json (the fleet compliance record) with a shadow score because default_artifact_path scopes by branch and bypass mode but not by pack (live for ~25 min tonight, restored); the incremental cache key omits the pack so alternating packs evict each other into cold full scans. Pins named, fix dispatched next. README gains the verb. Owner evidence: 3347 passed / 35 skipped / 10 xfailed both rootdirs; checklist 34/34 on all five new Python files; seedgo audit back to 100 across 47 standards after a readme flag their own restored audit caught; ruff clean, pyright 0. Devpulse: checklist 34/34 re-run on both new code paths, schedule and gitignore read. v4 untouched, nothing deleted.
…by pack (FPLAN-0478, @seedgo's cure for the two defects it reported from FPLAN-0476). default_artifact_path gains a pack argument on the rule bypass mode already used: audit pytest_quality writes last_audit_pack_pytest_quality.json and .seedgo/last_audit.json keeps the 47-standard aipass compliance record - proven by re-running the exact clobbering command, fleet-record mtime unchanged. The inline cache key became branch_audit.cache_key_for(branch, pack_path, no_bypass) and folds the pack in the way current_stamp already did, so alternating audit aipass and audit pytest_quality no longer evicts each other into a cold fleet scan. Counter-arms pinned on purpose: the aipass pack keeps the bare artifact name and the bare cache key (renaming the record or suffixing every key would fix the collision by breaking what it endangered). Six pins red-first, all in the two EXISTING test files (test_audit_artifact.py, test_incremental_audit.py) - no new test files; the full suite caught one stale expectation (test_delete_file_drops_from_cache_and_output hardcoded the bare key) now derived through the helper. Owner: 3353 passed / 35 skipped / 10 xfailed both rootdirs, checklist 34/34 + 23/23, ruff + pyright clean. Devpulse: 89 passed on the touched files, ruff + format clean, pyright 0. CHANGELOG line under the clampdown Fixed section. Flagged not chased: shadow artifact reports 11 scored where last night's clobbered file showed 12 (docstring_pin ships unscored).
…moved with Patrick's go (10:38), every branch still at 100. (1) tests/test_scaffold.py deleted in all 17 branches that carried it: one byte-identical birth-stamped smoke test, self-skipping in 8 of 17 since those branches wrote their own conftest; spawn update never re-adds it, newborns still get the citizen template copy (which carries canary's inspect.stack pin). (2) test_validate_valid_config / _data removed from api, devpulse, drone, seedgo, skills, spawn - 12 template stamps of one happy-path case, subsumed by seedgo's contract suite (ten-case matrix over every branch's implementation). (3) the two zero-assertion test_reimport_after_mock copies in drone and seedgo - the campaign's smoking gun, their only effect was placing importlib.reload in a v4-scanned file. (4) devpulse's 7 gitignored hook-engine POC return-value tests (local move, not in this diff). Per Patrick's ruling every removed text was moved to the branch's tests/.archive/ (gitignored disposal, local only - git history is the record); measured first that pytest never descends into dot-dirs (612 collected before and after a probe copy) and v4 scans only files directly inside tests/. Ground truth: re-measured on fresh copies of all 17 branches with the whole slice applied, test_quality 100 -> 100 everywhere (the dossier had measured function deletion; scaffold files hold one test, so whole-file deletion was re-measured); real fleet audit after the move: 100 on every CI-scored standard on all 18 branches (four 99s are trinity, machine-local, never scored in CI). 249 passed in the six edited files, all 17 branches collect clean, ruff + format clean. Group-2 comment counts corrected 10 -> 8 in three files. Dossier correction: the scaffold generator is spawn/templates/citizen/tests/test_scaffold.py, not aipass/shared/scaffold_content.py. Not in this slice: 38 test_json_durability copies wait for phase 4's parametrized replacement; the one parked row stays. Pre-existing checklist flags in the edited files (naming, silent_catch on untouched lines) left for their owners. Path-scoped: three test files modified by canary this morning (api test_host_perf, commons test_import_dead_cwd, prax test_repo_root) are live WIP and NOT in this commit.
…orrected: the two api driver test files are gitignored by name (api/.gitignore:15-16, DPLAN-0133 private-integration layer), so they have never existed on a CI runner and CI has never collected them - the earlier text had it backwards. The corrected finding: 25 test functions (13 devto + 12 bluesky, counted by AST) run locally that no CI leg can ever run. @aipass verified devpulse's correction independently before editing (post-kill redispatch, 14:46); committed by devpulse as it landed.
…ability contract over every _replace_with_retry in the fleet (FPLAN-0479, @seedgo). 86 parametrized cases added to the EXISTING tests/test_json_handler_contract.py: six behaviours (helper exists and is declared bounded; moves the staged file over the target; retries through a transient PermissionError; bounded retry raises when exhausted; waits between attempts; a non-PermissionError propagates with no retry) x 14 implementations, plus two guards against the failure mode a consolidation creates (discovery that matches nothing would collect zero and stay green; a canonical handler calling os.replace without the bounded helper is reported, not merely absent). Discovery is an rglob for the helper, not a glob of known handler paths - which found a FOURTEENTH implementation nobody listed: spawn/apps/handlers/atomic_write.py. Red first on all six by mutating seedgo's own handler (attempts=1, backoff=0, sleep deleted, catch OSError, bound removed, no-op replace - every one caught). Finding: NO divergence in the helper - all 14 assertion-identical (one docstring-normalised AST hash, 40 attempts x 0.005s), the opposite of save_json's 10 xfails over 4 classes, so no divergence table was added on purpose. Windows leg runs the same 14 cases (pure monkeypatch, no platform branch). Subsumes 37 of the dossier's 38 durability twins; the 3 not subsumed are real tests (commons drives save_json to exhaustion; spawn's two exercise atomic_write_text with stray-temp + survival assertions) - the 37 deletions wait for Patrick's go. Slice 3 scoped report-only: 24 more copies across four identities, fragmented by public-writer calling convention (largest family 3); three fit, concurrent-writers should be written once, not folded. Owner: 3435 passed / 35 skipped / 10 xfailed both rootdirs, checklist 23/23, ruff + pyright clean, seedgo audit 100. Devpulse: 265 passed on the file, 86 retry nodes, ruff + format + pyright clean. No new test files, nothing deleted, one file changed. Flagged not chased: import aipass binds the aipass BRANCH from src/aipass/ cwd (FPLAN-0474 species); test-write gate refused two read-only scripts on path-like strings. CHANGELOG under the clampdown Added section.
…bility twins removed on Patrick's go (keep powering on, 16:57), every branch still at 100. The six _replace_with_retry tests in tests/test_json_durability.py of aipass, backup, devpulse, drone, flow, prax (36) plus commons's test_retry_waits_between_attempts (1) - each now pinned by seedgo's durability contract (55cbff8) against all 14 implementations instead of one each. The three dossier rows seedgo showed are real tests stay: commons's bounded-retry drives save_json to exhaustion; spawn's two exercise atomic_write_text with assertions the twins never made. Same ruling as slice 1: each removed block moved verbatim to the branch's tests/.archive/ (gitignored disposal, local only, does not run); the errno import dropped in six files where nothing else used it. Verified: 7 files ruff + format clean, 48 passed across the seven files, all seven branches collect clean, drone @seedgo audit aipass per branch: Test_Quality 100 on all seven (devpulse/prax 99 overall = trinity, machine-local, never scored in CI). Remaining per file: aipass 4, backup 4, commons 13, devpulse 4, drone 4, flow 4, prax 15 - the write-site durability tests stay, they are slice 3's subject (FPLAN-0481, seedgo in flight). Pre-existing silent_catch flags in those files (except/pass on write-site tests, untouched lines) left for their owners. Canary's three dirty test files (api test_host_perf, commons test_import_dead_cwd, prax test_repo_root) remain live WIP, not in this commit. CHANGELOG under the clampdown Removed section.
…lity as one contract over all 18 branches, the concurrency race written once (FPLAN-0481). 96 parametrized cases in the EXISTING seedgo/tests/test_json_handler_contract.py, no new file. Four public-writer contracts x 18: the write routes through the bounded helper (counted on the HELPER, not os.replace - a syscall counter passed the exact inlining refactor the test forbids, rewritten mid-build); an exhausted retry leaves the original intact and cleans the staged temp; the writer survives a transient sharing violation end to end; a write that cannot land never answers True (added beyond the brief: three twins pin the return value and the fleet splits 9 raise / 8 return False, so the claim both camps satisfy is pinned rather than a coin-toss majority). Plus concurrent-writers-never-tear ONCE with skip-vs-fail discrimination: a race that did not race SKIPS with its counters, a torn or empty document is RED regardless, bounded join so a deadlocked writer fails instead of hanging the suite. Owner resolution walks out from wherever save_json is defined - six branches do not perform their own rename (aipass, canary, memory, spawn via aipass/shared; trigger via trigger/apps/config.py). Two findings: (1) a FIFTEENTH bounded helper - trigger/apps/config.py exports it PUBLICLY as replace_with_retry, invisible to a scan keyed on the private spelling; discovery matches both spellings now, +6 cases, slice 2's count corrected 14 -> 15. (2) ai_mail's save_json is not atomic: open(path, 'w') + json.dump, no staging, no rename, no retry reachable - the contract REPRODUCED a reader seeing an empty mailbox document six times in one four-writer run, and the except Exception reports the loss as a soft False. Four strict xfails name the branch; @ai_mail dispatched for the cure in their own tree. Red first on seedgo's own handler: helper inlined to a bare os.replace -> 2 failed; truncating in-place write (ai_mail's shape) -> all 4 failed; staged temp left behind -> 1 failed. Stability: 30 runs of the race across both rootdirs with zero variation, 5 consecutive full-suite runs. Owner: 3526 passed / 35 skipped / 15 xfailed both rootdirs, checklist 23/23, ruff + pyright clean, seedgo audit 100. Devpulse: 356 passed / 31 skipped (all pre-existing) / 15 xfailed on the file from both rootdirs, ruff + format + pyright clean, ai_mail's write shape confirmed in the source. Subsumes all 24 remaining durability twins across 7 branches - deleted in the next commit. CHANGELOG Added line under the clampdown section.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hw2gQBYHFHLnyYPCT7UC8C
… durability twins removed under Patrick's standing go, four files with them, every branch still at 100. The four write-site identities seedgo's FPLAN-0481 contracts (0c0cc51) subsume - test_atomic_write_routes_through_the_replace_helper (5 copies), test_exhausted_retry_leaves_the_original_intact_and_cleans_the_temp (6), test_save_survives_a_transient_sharing_violation (6), test_concurrent_writers_never_expose_a_torn_document (7) - removed from tests/test_json_durability.py in aipass, backup, commons, devpulse, drone, flow and prax; seedgo's per-copy subsumption list checked line by line, every assertion (len(calls) == 1, the is True / is False return pins, not stuck) has its match in the contract. In backup, devpulse, drone and flow nothing but fixtures remained after slices 2 and 3, so those four files go whole. aipass keeps its write_json routing pin (a sole carrier by name, outside the 24), commons keeps its exhaustion and source-guard tests, prax keeps the AIPASS_TEST_LOG_DIR seam class - each with a docstring pointing at the contract; dead helpers, fixtures and imports dropped. Per the archive ruling every removed text was moved verbatim to the branch's tests/.archive/ (gitignored disposal, local only - git history is the record). Ground truth: seedgo audit re-run on all seven branches after the change, test_quality 100 -> 100 everywhere; 24 tests pass across the three edited files; all seven branches collect clean; ruff + format clean. Phase 7 totals: 99 tests removed over three slices (38 + 37 + 24). Pre-existing checklist flag (silent_catch on commons's import shim) left for its owner. Path-scoped: canary's three live WIP test files are not in this commit.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hw2gQBYHFHLnyYPCT7UC8C
…-0483) and ai_mail's atomic mail-store write, landed together on purpose - either alone is six red contract cases. SEEDGO: the json_handler template stamp judged copy by copy against the contract suite - 201 copies (AST count; the dossier said 193) in tests/test_json_handler.py of api, drone, seedgo, spawn and devpulse's test_json_handler_template.py: 195 SUBSUMED, 5 NOT A TWIN, 1 STAYS. Fifteen contracts folded into the EXISTING seedgo/tests/test_json_handler_contract.py, each over all 18 branches (default document passes its own validator; default factory refuses an unknown json_type; ensure_json_exists creates / reports True / preserves valid / regenerates unreadable / regenerates structurally invalid; ensure_module_jsons creates all three / reports True; log_operation appends timestamped + True / attaches data / accumulates in order / rotates to the DECLARED cap; save_json reports True / writes a document that parses from disk) plus five VALIDATION_MATRIX tuples. New divergence tables, measured: ENSURE_RETURNS_NOTHING + ENSURE_ALL_RETURNS_NOTHING (seedgo returns None by documented decision), UNKNOWN_TYPE_NOT_REFUSED (skills writes null for a typo'd type), DECLARED_LOG_CAP_IGNORED (aipass/shared publishes max_log_entries and rotates on a class constant - aipass, canary, memory, spawn advertise a knob that does nothing). NOT A TWIN: test_save_rejects_invalid_structure is one fingerprint with two opposite claims (api returns False, four raise) - a ruling, not a consolidation. STAYS: spawn's fifo_rotation is the only live pin while spawn sits in the cap table. Finding: drone's and devpulse's fifo_rotation copies NEVER RAN (cap patched on a module attribute no branch defines). AI_MAIL: save_json wrote open(path, 'w') + json.dump - truncation on open, so a failed dump destroyed the live document while answering False; reproduced on the real handler (101-byte inbox -> 83 bytes of garbage). Cure: _atomic_write_json (mkstemp beside the target, fdopen, dump, flush + fsync, _replace_with_retry copied verbatim with the fleet's constants, temp removed on any failure) in save_json AND in ensure_json_exists, where the new AST source guard convicted a second truncating write nobody named. Disposition: helper raises like the fleet's, save_json catches and answers False. Two pins + two controls in the EXISTING ai_mail/tests/test_json_handler.py, 4/4 mutants killed. Seedgo dropped ai_mail from three divergence tables while both were awake; WRITER_HAS_NO_BOUNDED_RETRY and TORN_DOCUMENT_OBSERVED are now empty and kept with their record. Evidence: contract file 597 passed / 65 skipped / 16 xfailed from both rootdirs (devpulse re-ran); seedgo full suite 3767 / 69 / 16 / 0 failed both rootdirs; ai_mail 1449 passed both rootdirs, contract -k ai_mail 30 passed / 8 skipped; checklist 23/23, 34/34, 23/23; ruff + format + pyright clean on all three files; audits 100 (seedgo's 99 overall is trinity, machine-local). Two CHANGELOG lines (Added slice 4, Fixed ai_mail). Flagged for @hooks: the test-write gate resolved a pytest ARGUMENT in a cd-compound against the branch cwd and refused a read-only run as a new test file. The 195 deletions follow in the next commit. Not in this commit: canary's three live WIP test files.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hw2gQBYHFHLnyYPCT7UC8C
…ndler template stamp removed from api, drone, seedgo, spawn and devpulse under Patrick's standing go: 184 tests gone, 12 kept as v4 carriers, every branch still at 100. The 195 copies seedgo judged SUBSUMED (FPLAN-0483, 994465a - the same claim pinned for the branch in seedgo/tests/test_json_handler_contract.py, no skip or xfail row) removed from tests/test_json_handler.py in api, drone, seedgo, spawn and from devpulse's tests/test_json_handler_template.py; dead helpers (_get_default_for_type, _has_default_factory, _default_factory_raises_on_unknown), spawn's orphaned skip markers and unused imports dropped with them. The re-audit found what the dossier's sole-carrier measure never covered, because tier D was scoped to parametrize, not delete: in devpulse and spawn the stamp copies were the LAST text in tests/ carrying the v4 json_handler substrings - devpulse 100 -> 80 (default factory, validate, get_path, ensure_exists, load, ensure_module, plus the config-key, data-key and dict-return items), spawn 100 -> 90 (validate, get_path, ensure_exists, load, ensure_module) - while api, drone and seedgo carry them in other files. Seven copies restored in devpulse (JH-001 and JH-002 re-pointed at devpulse's own _default_config / _default_data since the cross-branch resolver went with the stamp) and five in spawn, each in a labelled 'v4 sole carriers - SUBSUMED, kept for the gate' block that goes the day v5 replaces v4, the same standing as the dossier's 24; the restored blocks dropped from the archive excerpts so each archive stays a true record of what is gone. Net: 184 removed (api 38, drone 40, seedgo 38, spawn 34, devpulse 34); remainders api 1 / drone 13 / seedgo 14 / spawn 7 / devpulse 8 (the copies seedgo did not subsume plus each file's branch-specific tests). Ground truth: seedgo audit Test_Quality 100 on all five after the restore (devpulse Overall 100 too - the trinity flag was my own local.json over the session cap, trimmed); 43 tests pass across the five files; all five branches collect clean (api 1593, drone 1300, seedgo 3814, spawn 941, devpulse 566); ruff + format clean. Per the archive ruling every removed text is in the branch's tests/.archive/ (gitignored disposal, local only - history on dev is the record). Phase 7 totals: 283 tests removed over four slices (38 + 37 + 24 + 184). Pre-existing checklist naming flag on the template's json_handler alias (all five copies, DPLAN-0059 lineage) left as found. Path-scoped: canary's three live WIP test files are not in this commit. CHANGELOG Removed line under the clampdown section.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hw2gQBYHFHLnyYPCT7UC8C
…3 and the phase total 283 -> 282 - ground-truthed on the files (226 test functions across the five before, 43 after: devpulse removed 33, not 34, since seven carriers went back, not six). The message of e95ec83 carries the slip; the CHANGELOG line now says so and is the record. e95ec83 board green (11th in a row on PR #751); FPLAN-0483 closed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hw2gQBYHFHLnyYPCT7UC8C
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant