A continuation of Janady13/missionmatch / docs/BUILD_STATE.md. The complete source identity, every recorded occurrence, and linked equations are on its first page.
Source transcript · continued
Download complete source text ↓3,723 lines · 322.2 KB
Credential-bearing and unrelated personal details were removed from this reading copy. The source identity and original checksum remain recorded.
afterwards is missing exactly when the process dies mid call. RecordExternalOutcome then records WHICH backend
actually served, because the gateway falls back to the local runtime on failure and that fallback is invisible
in an audit trail that records only intent.
MUTATION CONTROLLED, four new tests: removing the write fails exactly the two that assert it and restoring it
turns them green. Also asserted rather than trusted: no insert argument carries the payload content, and a
refused payload writes NO row at all.
IT EARNED ITSELF IMMEDIATELY. The first run RecordExternalOutcome ever saw was the run it caught: three
outcome rows reading ollama:llama3.1:8b after OpenAI refused with insufficient_quota. Without it the run would
have read as three successful external calls forever, because accepted=3 rejected=0 and three cited scope
items is exactly what a successful external run looks like from here. | 52fcbb9 | 2026-07-29
FINDING D7, THE D5 ROTATION LEFT TWO CONSUMERS STALE AND PRODUCTION HAS HAD NO TENANT BACKUP FOR A DAY
Found by checking production health rather than by an alert, which is itself part of the finding.
THE SYMPTOM: four units on Bubbles are in a failed state, mm-db-backup, mm-sam-sync, mm-awards-sync and
mm-rates-sync, all four with ONE root cause, password authentication failed for user
missionmatch_migrator against 100.94.8.94 port 5432, which is cluster 1, the production tenant.
PROVEN BY ROLE NAME AND STATUS CODE, never by printing a DSN, per the D6 rule:
api.env MM_MIGRATOR_DATABASE_URL OK missionmatch_migrator on missionmatch port 5432
backup.dsn FAIL password authentication failed
sam-sync.env MM_MIGRATOR_DATABASE_URL FAIL password authentication failed
So the ROLE IS FINE and its current password is the one api.env already holds. Two files hold a STALE
COPY. This is the exact shape of finding D3, which was diagnosed the same way and fixed by correcting
the stale copy rather than by rotating the role. Rotating here would break the working consumer.
EVERY OTHER CONSUMER ON BOTH NODES IS HEALTHY, checked individually: api.env control plane app and
compliance, staging.env staging_migrator and staging_app with the D6 isolation still holding, and on
megamind-agi all four db.env roles, backup.dsn, api.env, and BOTH mm-identity-sync DSNs across BOTH
clusters, which was the trap D6 named and it held. Identity sync ran clean at 16:04.
THE IMPACT, measured from the backup directory and the journal rather than asserted:
last COMPLETE backup set 28 July 16:42:58, tenant and staging, with its MANIFEST.sha256
failed runs since 29 July 03:07:53, 06:00:06, 12:00:08, three consecutive
left behind three ZERO BYTE tenant dump files in daily/20260729/
control plane and staging dumps for 29 July do not exist at all, because the script dies on the
first dump and never reaches them
So the largest recorded risk in the register, the absence of a backup, is LIVE right now and has been
for roughly twenty four hours. The zero byte files are partials and must be removed, because a restore
that finds them is a restore that reads a truncated dump.
sam-sync.env IS NOT IN docs/CREDENTIAL_CONSUMERS.md. That file exists precisely to be complete, and D6
rule nine says a consumer discovered outside it is a defect IN THE FILE to be fixed in the same change.
backup.dsn IS listed and was still missed, so the rotation did not enumerate from the file it wrote.
NOT FIXED, because it is a production configuration change and production scope is closed outside the
approvals in the standing order. QUEUED for Joseph. It also BLOCKS both halves of standing order item 3:
the deploy procedure requires a fresh backup of the affected cluster first, and the storage restore
drill needs a backup to restore.
FINDING D8, THE FROZEN ENGINE DRIFT CHECK IS ANCHORED TO THE WRONG COMMIT, THE SECOND FAIL OPEN IN THE
SAME CHECK. The most load bearing check in the protocol is still not trustworthy as documented.
MEASURED: the diff of internal/modules/matching/engine against the documented anchor 9e77f65 is 1476
LINES, not zero. Against 864a999, 9 July, the last commit that legitimately touched the engine, it is
ZERO. Against e74fd2e, 27 July, the first commit of this build, it is ZERO.
THE CAUSE: 9e77f65 is dated 24 JUNE 2026 and titled Baseline, missionMATCH platform plus Phase 2
Authentication and Accounts. It is the ORIGINAL PROJECT baseline, not the pre build baseline. The engine
was legitimately developed after it, through ModelVersion v1.1.0 to v1.4.0 across 26 and 29 June, 1
July and 9 July. A diff against 24 June therefore CANNOT be empty and never could have been.
WHY THE PRIOR RUN REPORTED ZERO ANYWAY, and this is the part worth keeping: when the history purge
rewrote every hash, the anchor was repointed to 9e77f65 and the check was reported as verified at zero.
The real path against that commit yields 1476, so that zero did not come from this path and this
commit. Several plausible engine paths DO NOT EXIST in the tree, internal/platform/scoring,
internal/scoring, internal/engine and pkg/engine, and git diff against a path that does not exist
prints nothing and exits zero. That is the identical fail open shape as the missing commit, one layer
over: the first fix moved the failure from a nonexistent COMMIT to a nonexistent PATH.
THE FREEZE ITSELF HOLDS, stated as plainly as the defect. The engine has not changed since 9 July,
eighteen days before this build started. Nothing this build did touched it. Acceptance 23 is satisfied
in substance and the artifact is intact. What failed is the instrument that proves it.
THE REPAIR, queued rather than done because it changes a gate definition: anchor the check at e74fd2e,
the build's own first commit, which states the claim the gate actually wants, that THIS BUILD changed
nothing. Assert the anchor commit exists AND that the path exists and is non empty before trusting a
clean result, because both have now failed open once each.
D7 CLOSED BY REPAIR, AND IT WAS THREE STALE CONSUMERS NOT TWO | 29 July 2026. JOSEPH'S APPROVAL, recorded
as the guardrails require: he approved the full repair, credentials plus partial cleanup plus a proven
backup, in answer to a proposal that named the config only scope and the absence of any ALTER ROLE.
METHOD, deliberately the D3 method rather than a rotation: the role was never wrong, so the WORKING value
already in api.env was copied into the stale files. Rotating would have broken the one consumer that
worked. No ALTER ROLE, no cluster scoped object touched, so rule one did not apply.
A THIRD STALE CONSUMER WAS FOUND DURING VERIFICATION, which is why the fix is not simply two files:
sam-sync.env also carried a stale missionmatch_compliance password. It was not in the first diagnosis
because the first probe only tested the migrator variable in that file. Found by widening the probe to
every variable rather than the ones I expected to be wrong.
PROVEN AFTER, by role name and status code, never by printing a DSN, eleven consumers across both nodes:
backup.dsn OK, api.env migrator app compliance and tenant_acme all OK, sam-sync.env migrator AND
compliance now OK, staging.env all three OK on their own database.
ONLY THE INTENDED LINES CHANGED, proven not asserted: sam-sync.env stayed at 11 lines with 11 variable
names, and a diff of the file EXCLUDING the changed line reported 0 differences, twice, once per repair.
Originals backed up root owned 0700 before either write. Every write staged to /tmp and cp'd, because
sudo tee truncates.
D6 CONTAINMENT RE PROVEN, and MY OWN CHECK WAS WRONG FIRST. The first containment test reported
"password authentication failed for user missionmatch_migrator", which looked like a pass and was not: my
sed pattern matched the USERNAME inside the DSN, so it tested a role that does not own the staging
password against the staging database and proved nothing. Corrected to substitute only the database after
the port, after which missionmatch_staging_migrator against the PRODUCTION database is refused with
"permission denied for database missionmatch", a privilege denial rather than an auth failure, which is
the correct refusal and is what D6 built. Recorded because an instrument that lies in my favour is the
thing this ledger exists to catch.
THE BACKUP IS REAL AGAIN, which is the only proof that counts: mm-db-backup Result=success, tenant dump
17209805 bytes, staging 2957058, control plane ingested 97184, manifest 3 artifacts and sha256sum -c OK
on all three. Three zero byte partial dumps from the failed runs were deleted, size 0 only, and zero
remain anywhere under the backup root. All four units reset, zero failed units.
ALL THREE SYNCS PROVEN END TO END, not merely authenticated: sam-sync upserted 1420 with the store at
2614 opportunities, awards-sync 525 with the store at 538, rates-sync 20 with the store at 1670.
THE INVENTORY DEFECT FIXED IN THE SAME CHANGE per D6 rule nine: docs/CREDENTIAL_CONSUMERS.md gains both
sam-sync.env rows, and records that one stale line there stops THREE units at once. It also records the
harder lesson, that backup.dsn was ALREADY LISTED and was still missed, so a complete inventory is
necessary and not sufficient. The rotation step that failed was step one, read this file, not a step
that was absent.
HOW LONG PRODUCTION HAD NO BACKUP, stated plainly: last complete set 28 July 16:42:58, first success
after the repair 29 July 16:24:26, so roughly twenty four hours with three failed runs in between. The
alerts DID fire, OnFailure was wired for all four units by the 28 July config deploy, so this was not
invisible in the D2 sense. It was unread.
D8 CLOSED BY CORRECTION, AND THE SAME WRONG HASH WAS ALSO THE DEPLOY BASELINE | 29 July 2026, approved by
Joseph, anchor at e74fd2e.
THE SECOND USE IS THE DANGEROUS ONE AND IT HAD NOT BEEN NOTICED. 9e77f65 was named in TWO live places:
the frozen engine drift anchor AND the HOW TO DEPLOY worktree baseline that every production binary is
built from. The engine anchor being wrong made a gate report a result it had not measured. The DEPLOY
BASELINE being wrong would have silently REGRESSED FIVE WEEKS of shipped product work on the next
deploy, including engine ModelVersion v1.4.0, the audit_logs row level security, and the identity sync
host fix, because 9e77f65 predates all of them. Production is not affected: it runs 8f24414531e80a9a,
built from the REAL 462eb81 before the purge. The next deploy would have been affected, and the next
deploy is the fairness snapshot deploy in the standing order.
THE TRUE PRE BUILD BASELINE IS 2178e72, 21 July, which matches this ledger's own description of 462eb81
as the 21 July edition. e74fd2e is the build's first commit and adds ONLY a 145 line spec document over
2178e72, so it is code identical to the true baseline, carries ZERO build feature files, and gives an
engine diff of zero. One anchor is therefore correct for both uses and states the claim the gate wants.
CORRECTED IN THE LIVE INSTRUCTIONS ONLY: BUILD_CONTINUATION_PROTOCOL Gate D, the BUILD_STATE deploy
block, and three lines in ARCHITECTURE.txt. Historical narrative entries KEEP the hash they were written
with, because they were already rewritten once by the purge and rewriting the record a second time to
match a later understanding is falsification, not correction.
THE CHECK NOW REFUSES INSTEAD OF PASSING QUIETLY. It asserts the anchor commit exists AND that the path
is non empty at both ends before trusting a clean result. BOTH FAIL OPEN MODES DEMONSTRATED: a diff
against a nonexistent commit prints 0 lines, and a diff against a nonexistent path prints 0 lines, each
indistinguishable from success without the assertion. Detector proven live at 120 lines on a file that
did change. The freeze itself always held: the engine is unchanged since 9 July.
SECTION 2.2 DONE, THE FAIRNESS SNAPSHOT PRODUCTION DEPLOY | 29 July 2026, under the standing order's
approval. Fresh backup first, taken 16:24:26 with its manifest verified, per the deploy procedure.
A FOURTH D5 CONSUMER FOUND, AND IT IS NOT A FILE. migrate-all-tenants fans out to each tenant by REUSING
the control plane migrator CREDENTIAL against the tenant's own host, which works only while the two
clusters' migrator passwords are IDENTICAL. The D5 rotation changed cluster 1 and not cluster 2, so the
fan out died with a SASL auth failure on the tenant database while the control plane leg was fine. The dry
run caught it before any write, which is what the dry run is for.
FIXED WITH THE TOOL'S OWN DOCUMENTED MECHANISM, not by re coupling the clusters:
MM_TENANT_MIGRATOR_DSN_TENANT_ACME added to db.env on the control plane node. tenantDSN's own comment
names this exact case, a tenant whose migrator credential differs because of a rotated password. The
alternative, making the two passwords match again, would have restored the cross cluster coupling that
caused D6, so it was deliberately not chosen. The value moved host to host through stdin, never through
argv and never printed, and only the one variable changed, proven by a diff excluding it reporting 0.
MIGRATIONS APPLIED, both targets asserted by role name, database and port BEFORE any write:
control plane public v80 to v83 (+3), eeo unchanged at v2
tenant public v80 to v83 (+3), eeo unchanged at v2
0083's function eeo_snapshot_organizations present on BOTH, verified by pg_proc count.
BINARY DEPLOYED. Production had been running the 30 JUNE build, which predates the D1b fix entirely, so
the code fix had never been in production at all. New binary 740a432dba3bbdef, ELF x86-64 with
GOFIPS140=v1.0.0 stamped, previous binary preserved at /root/fairness-snapshot.pre-0083.
D1b IS CLOSED IN PRODUCTION. Before: "no organizations to snapshot" at every firing since the unit
existed. After: "1 orgs snapshotted". The enumeration works.
AND THE ARTIFACT IS STILL EMPTY, WHICH IS STATED RATHER THAN ROUNDED UP. 0 group rows were written, and
the reason is DATA, not code: eeo.candidate_demographics holds ZERO rows in production, so there is
nothing to compute a selection rate from. Measured: candidates 11, demographics 0, organizations 1. On
staging the same binary wrote 83 group rows because staging carries demographic data. So the honest claim
is that the JOB is fixed and the ARTIFACT awaits demographic data, and missionMATCH still cannot hand an
assessor a populated LL144 report for production today.
A SEPARATE OBSERVATION, recorded not acted on: eeo.fairness_snapshots already holds 2760 rows spread
across 46 organization ids while the organizations table holds ONE. That is residue from the pooled era
before the per tenant split. It is stale data in a compliance table, which is worth a decision.
THE INDEPENDENT ASSERTION PROVEN TO FIRE IN PRODUCTION, as the standing order requires, run as a MUTATING
REHEARSAL WITH ITS REVERT IMPLEMENTED AND VERIFIED FIRST per D6 rule four:
captured the real function definition, 730 bytes, sha b0e81e17768da4d1
stubbed it to return no rows while organizations still held 1
the job REFUSED and exited 1, naming the D1b failure mode and saying a compliance artifact must never
silently produce nothing
reverted from the captured definition and PROVED the revert byte identical by diff
counted run after the control: Result=success, 1 org snapshotted
A function is database scoped, not cluster scoped, so rule one did not apply to this rehearsal.
BUILD ROUTES RE PROVEN DARK AFTER THE DEPLOY, four samples each so both pool members answer: approve 404,
scope 404, tracked 405. Positive control GET /api/auth/csrf 200, so a uniform 404 cannot be a dead host.
Site 200, both node healthz 200. The API binary was NOT touched, only schema and a worker binary.
SECTION 2.3 DONE, THE DISK ALERT AND THE BACKUP TARGET ON VALKYRIE | 29 July 2026, under the standing
order's approval, which named VALKYRIE and superseded the A1 and A2 choice in the storage proposal.
PART ONE, THE 85 PERCENT ALERT, BUILT AND PROVEN. /usr/local/sbin/mm-disk-watch.sh with a 15 minute timer,
WARN at 85, URGENT at 92, alerting through mm-alert.sh, naming the top five consumers in the body so the
mail says what to delete. It alerts on a TRANSITION following the mm-watch.sh pattern, because a watch that
mails every fifteen minutes trains the reader to ignore it, which is the same outcome as no watch.
OnFailure wired to bubbles-failure-notify@%N, matching the convention the eight existing units use rather
than a new template, and scoped by name so another project's failures cannot ride this path.
POSITIVE CONTROL IN BOTH DIRECTIONS, exactly as the proposal specified: with the threshold forced to 1 the
watch reported warn, ALERTED, and exited 1; restoring the real threshold alerted the RECOVERY and returned
to ok; a second run at real thresholds was SILENT, proving the transition logic and not just the alert.
Both mails confirmed SENT in the notifier's own send.log, not merely handed off. Real reading 82 percent,
below threshold, timer active and enabled.
PART TWO, THE BACKUP TARGET IS NOW OFF MACHINE. mm-db-backup.sh gained a verified push to VALKYRIE,
beandaddy@100.89.0.6, into missionmatch-backups. PURELY ADDITIVE, proven: 43 lines added, 0 removed,
bash -n passing, previous script preserved at /root/mm-db-backup.sh.pre-offsite.
THE LOCAL COPY STAYS. It restores in seconds and that matters during an incident. What changed is that a
root disk failure no longer takes the platform and every copy of its backups together, which was the
actual risk in the proposal and the reason correlation beat capacity as the concern.
IT VERIFIES THE FAR SIDE BY CHECKSUM rather than trusting the transfer's exit code, because this build has
already met a push that reported success while three zero byte dumps sat on disk looking like backups.
Counted run: offsite OK, 7 of 7 artifacts verified on VALKYRIE, unit Result=success. Remote retention
prunes to 14 daily by count, POSIX only because the far side is a Mac.
THE FAILURE PATH PROVED ITSELF FOR REAL, WHICH IS BETTER THAN A PLANTED CONTROL. The first counted run
FAILED: the unit runs as ROOT and root had no authorized key on VALKYRIE, while my interactive test had run
as user. The new fail LAST assertion fired, the unit exited 1, and OnFailure triggered. That is F1 from the
Phase 2 trial in a new costume, a thing that works in one context and not under systemd, and it is the
third time this build has been caught by exactly that gap. Fixed by authorizing root's ALREADY EXISTING
public key rather than minting a new credential, appended not overwritten, 5 lines to 6.
PART THREE, THE RESTORE DRILL RERUN AGAINST VALKYRIE AND IT PASSED. The drill's root was made overridable
so it can be pointed at the off machine copy, because a drill that can only read the local copy cannot
prove the copy that would actually be used after losing this host.
Pulled the day's set BACK from VALKYRIE, 8 files, 58 MB, all verified against the manifest, then restored
from THOSE bytes: RESTORE TIME 11 SECONDS for 16 MB, schema source v83 and restored v83 which independently
confirms the 2.2 migration landed, candidates 11 reconciled, opportunity_records 6902 reconciled, and all
ten reconciliation rows OK. pg_restore reported 3 errors, which the drill surfaces and tolerates by its own
existing logic; recorded rather than omitted.
THE NIGHTLY ATTACHMENT SCHEDULE REMAINS OFF, and it is off BY CONSTRUCTION: no attachment timer unit
exists on the host at all, so there was nothing to disable and nothing was created. Both preconditions the
standing order set are now met, so enabling it is available and is deliberately left as Joseph's call
rather than taken as implied.
FINDING D10, A DEAD CREDENTIAL IN PLAINTEXT IN AN OPS SCRIPT, AND THE RESTORE DRILL HAS BEEN BROKEN SINCE
D5 | Found while running the approved drill, which is the only reason it surfaced.
mm-restore-drill.sh hardcoded a migrator DSN WITH THE PASSWORD IN PLAINTEXT on one line. That made it a
FIFTH D5 consumer, listed in no inventory, and one the repository secret sweep can NEVER see, because the
sweep scans the repository and this file lives in /usr/local/sbin. A credential store that only covers the
repository does not cover the credentials.
THE CONSEQUENCE: the D5 rotation left it holding the OLD value, so THE RESTORE DRILL COULD NOT RUN. The one
instrument whose entire purpose is to prove a backup is restorable was itself broken by a rotation, and the
protocol requires it at every phase close.
I REPORTED THIS WRONG FIRST AND THE CORRECTION MATTERS MORE THAN THE FINDING. My initial probe said the
hardcoded credential was STILL LIVE, which would have meant a burned credential active in production and
the whole D5 window pointless. It was MY INSTRUMENT THAT WAS WRONG: I tested by grepping the output for the
role name, and the failure message itself contains user "missionmatch_migrator", so a FATAL authentication
failure matched my success pattern. Retested with .pgpass explicitly disabled and by comparing salted
hashes of the password held by each source: the drill's value equals the PRE repair backup.dsn value and is
DEAD, api.env holds the distinct live value. So the D5 rotation DID work, and the correct reading is the
ordinary one, stale copies of a dead credential. Recorded in full because a check that matches its own
error message is the exact failure this ledger exists to catch, and I made it.
FIXED, AND HARDENED BEYOND THE REPAIR: the script no longer stores a credential at all. It reads
MM_MIGRATOR_DATABASE_URL from api.env and refuses to run if it is absent, so one file changes at a
rotation and this follows without anyone remembering it exists. Verified: zero credential shaped lines
remain in the file, and it resolves as missionmatch_migrator. Previous version kept at
/root/mm-restore-drill.sh.pre-d10.
STILL OPEN, and it is Joseph's: docs/CREDENTIAL_CONSUMERS.md covers files on two hosts and does NOT cover
/usr/local/sbin scripts on either, which is where two of the five broken consumers actually lived. The
inventory needs a section for ops scripts, and the secret sweep needs a way to see outside the repository.
FINDING D9, THE AMENDMENT VERSION COUNTER IS STUCK AND THE ERROR IS SWALLOWED | Found 29 July 2026 in the
first successful sam-sync run after the D7 repair, which is the only reason anyone saw it.
THE LOG LINE: amendment for opportunity afc5143e insert version: duplicate key value violates unique
constraint uq_opp_versions_number. The run still exits 0, because the design says the provenance write
never fails the run, so this has been failing silently since 27 July 22:28.
THE STATE IS WORSE THAN THE ERROR. opportunity_versions holds exactly ONE row for that opportunity,
version_number 1, is_current FALSE. So the predecessor was RETIRED and the successor was NEVER WRITTEN,
and the opportunity now has NO CURRENT VERSION. The retire and the insert were meant to be one
transaction and did not behave as one here.
IT IS SELF PERPETUATING, which is the part that matters. With no current row, the next version number
computes as 1 again, collides with the existing version 1, and fails forever. No amendment can ever be
recorded for that opportunity again without intervention.
SCOPE MEASURED, not assumed: 1 version row in the whole production table, 1 opportunity with versions,
1 with no current version, 0 with multiple current. So the breach is total for the only tracked
opportunity and contained to it.
WHY IT DOES NOT INVALIDATE ACCEPTANCE ITEM 11: item 11 asserts an amendment FLAGS and never REWRITES an
approved requirement, and that held in the Phase 2 trial on real amendment text with the md5 identical
before and after. This is a different guarantee, the is_current invariant on the version chain.
NOT FIXED. It needs an application change plus a decision about the one stranded row, both outside the
approved scope. QUEUED for Joseph.
## SUPERSEDED HANDOFF, 29 July 2026 evening, recovery session. Kept as a record, no longer current.
DEMOTED, NOT DELETED, and the demotion is itself the fourth instance of this problem. This was the current
handoff when it was written. A later session in the same day continued the work, which made it stale, and when I
added the new handoff at the top of this file I created TWO, which is precisely the defect the block below warns
about in its own first paragraph. The check for it, grep for the heading and require exactly one, found it within
a minute of the commit.
THE RULE THIS FINALLY EARNS: writing the handoff last is necessary and not sufficient. The old one has to be
demoted in the same edit, or the file grows a second source of truth every session. Superseded handoffs keep
their content and lose their title.
PHASE 2 IS NOT CLOSED AND THE PRE APPROVAL DID NOT FIRE. It is conditioned on zero open findings and
zero new deviations. This session opened FOUR findings, D7, D8, D9 and D10, and closed two of them. By
the standing order's own terms that is not a green close, so Phase 3 was NOT started and Phase 2
remains the one current phase.
DONE THIS SESSION, all verified, all recorded above
Reconstruction from the directive chain and the live systems. The stale handoff replaced, third time.
Standing order 1, 2 and 4 confirmed already done. All 16 package checksums match the order exactly.
Session start drift verification with every control firing. Evidence at
docs/evidence/phase2/drift/session-start-drift-4.txt
D7 CLOSED. Three stale credential consumers repaired, production backup restored after roughly
twenty four hours without one, three zero byte partials removed, all three syncs proven end to end,
CREDENTIAL_CONSUMERS.md corrected in the same change.
D8 CLOSED. The frozen engine anchor was the 24 June project baseline, and the same wrong hash was
also the DEPLOY baseline, which would have regressed five weeks of shipped work on the next deploy.
Anchored at e74fd2e. The check now asserts the commit and the path exist.
SECTION 2.2 DONE. 0081, 0082, 0083 to both production databases, v80 to v83. Fairness binary deployed,
replacing a 30 JUNE build that predated the D1b fix. Independent assertion proven to fire in
production as a mutating rehearsal with a verified revert. Build routes re proven dark.
SECTION 2.3 DONE. 85 percent disk alert built and proven both directions. Backups now pushed to
VALKYRIE and verified there by checksum. Restore drill rerun against the VALKYRIE bytes and PASSED,
11 seconds, schema v83 both sides, all ten reconciliation rows OK.
OPEN, AND EVERY ONE IS JOSEPH'S
D9 the amendment version counter is stuck on one opportunity, is self perpetuating, and its error is
swallowed by design. Needs an application change plus a decision on the stranded row.
D10 remainder: CREDENTIAL_CONSUMERS.md covers files on two hosts and NOT /usr/local/sbin scripts,
where two of the five broken consumers actually lived. The secret sweep cannot see outside the repo.
THE AUTHENTICATED PRODUCTION LEG WAS APPROVED AND WAS NOT RUN. Joseph approved provisioning and
retiring a probe account. I stopped short of starting it because it is a production identity change
and I judged the remaining context too short to finish it cleanly, and a half provisioned production
account is worse than none. This is the leg that previously caught a 500 hiding behind the session
gate, so it is a real gap and it is named rather than quietly dropped. IT IS THE FIRST THING TO DO.
THE NIGHTLY ATTACHMENT SCHEDULE is off by construction, no unit exists. Both preconditions the
standing order set are now met, so enabling it is available and is deliberately left as his call.
eeo.fairness_snapshots holds 2760 rows across 46 org ids while organizations holds one, pooled era
residue in a compliance table.
THE FAIRNESS ARTIFACT IS STILL EMPTY IN PRODUCTION and that is data, not code:
eeo.candidate_demographics holds zero rows, so there is nothing to compute a selection rate from.
STATE AT STOP, every number verified after the last change
401 commits, HEAD 6978143, tree CLEAN, stash empty.
53 packages ok and 0 FAIL, merge gate PASS with verify-backdoor and verify-fips both PASS.
Secret sweep CLEAN on tree and on history, control firing.
Engine diff 0 against e74fd2e, with the anchor and path assertions in place.
Tenant v83, control plane v83, staging v83, each queried directly, all three level.
Both production nodes 8f24414531e80a9a, identical, healthz 200 each.
Public site 200, build routes dark 404/404/405. Zero failed units on Bubbles.
Last backup 29 July, verified locally AND on VALKYRIE by checksum, restore drill passed against the
off machine copy.
Bubbles root disk 82 percent with the alert now live on a 15 minute timer.
## PHASE 2 SCOPE COMPLETION AND GATE RE RUN, 29 July 2026
PHASE 2 WAS LESS FINISHED THAN THE PREVIOUS GATE E CLAIMED, and the gap was SCOPE rather than verification.
Reading the directive against the code, rather than reading the ledger, found four requirements that did not
exist in any form. That is a different and worse category than the known limits the last Gate E listed.
SECTION 5.1 LISTS TABLES among the required deterministic extraction targets. Nothing read tables at all.
That is the actual reason the wage determination dropped 231 of 232 spans: the prose gate correctly refused
rows of codes and numbers and NOTHING ELSE WAS LOOKING AT THEM. It was recorded as a capability the phase
did not claim, when it was a capability the directive requires.
SCREEN 4 NAMES SIX REVIEW ACTIONS and four existed. 'merged' was a status value nothing ever wrote and split
had no representation of any kind. Migration 0084 adds the split status, widened from the CURRENT
vocabulary because this build already learned that lesson the expensive way.
SCREEN 5 REQUIRES GENERATING DRAFTS from accepted scope and nothing did, so the review to requirement path
had a hole in the middle of it.
SCREENS 3, 4 AND 5 HAD NO UI AT ALL, and neither did the amendment view, while Gate C names every one of
them. The scope review and promotion routes were mounted and UNREACHABLE, which is the same defect shape as
the promotion routes that were once built and never mounted.
All four are built, tested, and deployed to staging. Evidence at docs/evidence/phase2/functional/
gate-a-phase2-close.txt, stress/gate-b-results-2.txt, drift/gate-d-phase2-close.txt, and GATE_E.md rewritten.
TWO GATE 2 CRITERIA HAD NEVER BEEN EXECUTED. ingest_during_promotion_test.go sits behind a build tag and skips
without pools, so the 53 package suite never touched it, and it carried two of Gate 2's six clauses. Both now
run and pass: an ingest run during promotion corrupts nothing, and an approved requirement survived 20 ingest
rounds byte for byte at md5 6acbad7d5a24cb161b141e1d5a99e717. All six Gate 2 criteria are now proven.
THE REAL DOCUMENTS CAUGHT MY OWN WORK, TWICE, AND THAT IS THE PART WORTH READING.
THE WAGE DETERMINATION IS NOT RECOVERABLE BY THIS BUILD, AND IT NOW SAYS SO. Before: extraction done, 1
chunk stored. After: FAILED no_text_layer, 0 chunks. That is an IMPROVEMENT. The stored chunk was mojibake,
because that PDF's text layer decodes to character soup from an embedded font with no usable character map,
and reporting done over a garbage chunk is worse than failing because a reviewer cites it.
MY TABLE READER MADE IT WORSE FIRST. Against the real document it recovered "tables" of content like "(0 CC"
and "CC A3": mojibake contains tabs and therefore satisfied every structural test I had written. A structural
reader that BYPASSES the prose gate must carry its own quality bar or it becomes the hole in it. The bar is
the weakest thing that separates language from soup, a run of four letters containing a vowel.
THE SAME BAR CLOSED A PRE EXISTING HOLE: that mojibake had been passing the PROSE gate all along.
AND IT COST THE LEGITIMATE CASE NOTHING, measured on the same run: 04_PWS_original went 54 chunks to 55,
GAINING a table of 3 rows. Table extraction is proven by 7 unit tests on real shaped input and by the volume
profile parsing a 200 page PWS into 400 chunks, 200 tables and 1600 rate rows in 19 milliseconds.
F6 FIXED, AND MY FIRST FIX WOULD HAVE FAILED ON THE REAL DATA. Pairing purely within a document type looked
correct against a fixture I wrote where both PWS documents were typed pws. The intake actually classified the
amended PWS as an AMENDMENT, so pws held one document and the pairing fell straight through to the old wrong
answer. It now matches the amendment to the baseline whose FILE NAME it most resembles, version words stripped.
On the real Package B it pairs 04_PWS_original against 05_PWS_amended and reports ONE material change, quantity
1 to 7, flagging the one derived approved requirement. Previously five changes, partly artifacts. Also removed
attachment and other from the pairable types: an earlier version would have diffed an RFC log against a
deliverables list, which is worse than the bug being fixed.
PURE TESTS MISSED TWO REAL BUGS, EXACTLY AS F3 PREDICTED. Split, merge and generation shipped with pure
validation tests only, which is F3's shape precisely. Database touching tests found both in seconds: FOR UPDATE
used with an aggregate, which Postgres refuses outright, and a nil Go slice arriving as NULL in a NOT NULL array
column so generation failed on every document naming no certifications. Six integration tests now cover split,
merge, generation and manual completion, asserting the properties that matter: children inherit the citation,
generation from UNREVIEWED scope yields nothing, and a field the document never states is reported missing
rather than guessed.
GATE B EXTENDED WITH THE THREE PROFILES THE PROTOCOL NAMES AND NOBODY RAN: volume, concurrent-extract and
scrub-flood. Race at 8 and 32 both elect one winner, load holds p95 9.595ms against a 300ms target with zero
errors, scrub flood reports 0 misroutes and 0 false refusals across 1000 checks.
TWO OF MY OWN CONTROLS WERE BROKEN FIRST. The memory profile measured CUMULATIVE allocation, which is
identical whether results are retained or discarded, and its control returned an error UNCONDITIONALLY so it
reported success while detecting nothing. Its cap was then a number chosen from nothing, 4096 KB, which the
real leak came in under. The scrub control laundered payload CONTENT while keeping provenance and then blamed
the gate for correctly allowing a payload whose only private element had been stripped. Both fixed and both
recorded, because a control that passes by construction is the exact failure this protocol exists to catch.
FINDING D11, FOUND BY GATE D ITSELF. Migration 0084 FAILED on staging with "must be owner of table
opportunity_scope_items". The D6 structural fix created missionmatch_staging_migrator and mirrored GRANTS but
left every staging object OWNED by missionmatch_migrator, and grants do not permit ALTER TABLE, so staging could
not apply a DDL migration through the migrator that exists to migrate it. Fixed by explicit per object transfer
of 88 tables and 197 functions, deliberately NOT via REASSIGN OWNED, which also moves shared objects and could
have changed the PRODUCTION database owner as a side effect. Verified after: production tables still owned by
missionmatch_migrator, both database owners unchanged.
THE go:embed GOTCHA BIT AGAIN. The first production run of 0084 reported +0 because the migrator binary
predated the migration. The ledger already carried this lesson. Rebuilt, redeployed, +1 on both databases.
ALL THREE SCHEMAS ARE NOW LEVEL AT v84, queried per database. Production carries 0084 because it is additive
and carries no data, the same treatment 0081 through 0083 received, and the routes stay dark.
STAGING RUNS THE NEW BUILD: api 0fb3629d44d7b18a with the FIPS stamp, bundle index-g1gTV6We.js. index.html was
EDITED in place with only the two asset hashes rewritten, so all 22 lines and all 6 injected SEO lines survived.
All four new screens serve 200 over HTTPS and the shipped bundle contains every one of their strings.
THE AUTHENTICATED LEG RAN END TO END ON STAGING through a probe account whose password was set with
cmd/rotate-credential so it went through the real pepper and PBKDF2 path. Login 200, session 200, and every new
route 200: scope, requirement-drafts, amendments, documents, and generate. The amendment view returned the same
affected approved requirement the diff independently flagged. The probe's tool grants were restored afterwards.
TWO REFUSALS ALONG THE WAY WERE BOTH CORRECT. A 403 was the CSRF middleware refusing a request with NO Origin
header, which curl does not send: a hard Origin allowlist working as designed. The 404s that followed were the
TOOL ENTITLEMENT gate failing closed on an account with no slateMATCH grant, answering 404 rather than 403 so
a surface is indistinguishable from a path that was never built. Mistaking either for a defect would have sent
me chasing the wrong thing.
GATE C RUN FOR THE FIRST TIME AGAINST THE PHASE 2 SCREENS, because until today there were none to sweep.
Evidence at docs/evidence/phase2/visual/phase2-close/.
COUNTED RUN: surface authenticated, viewports 768, 1024 and 1440, 36 captures, ZERO findings. That is 6
routes by 3 viewports by 2 roles, the full matrix with nothing skipped, as seeded recruiter and manager
fixtures logging in through the REAL login flow with the slateMATCH entitlement.
THE CONTROL PROVED SOMETHING THE COUNTED RUN CANNOT. A planted 4000 pixel overflow was detected on every one
of the six routes at every viewport, and the planted minimum width regression with it. To report an overflow
on /slate/capture/{id}/scope the sweep had to log in, hold the entitlement, resolve the route and render the
screen, so a control that fails on all six routes is simultaneously proof that all six are reachable.
SHELL GUARD: the widest route needs 716px against a 732px baseline plus a 24px allowance, so the new screens
sit inside the shell's budget rather than on its edge.
VIEWPORTS ARE 768 AND UP for the authenticated surface, which is the recorded product decision of 28 July and
not a relaxation taken here. The public surface still holds all five widths.
TWO CONSOLE ERRORS did not reproduce on retry and were dropped to transient.json rather than counted or
hidden, so the decision to drop them is auditable.
PIXEL BASELINES REMAIN UNAVAILABLE BY PROTOCOL rather than by omission: a baseline becomes one only after
Joseph approves a contact sheet. The sheet from this run is in the evidence directory.
D9 IS NOW FIXED IN PRODUCTION, NOT ONLY IN CODE, and the stranded row healed itself. The fixed sam-sync,
75ad042b415cfd03, was deployed to Bubbles with the previous binary preserved at /root/sam-sync.pre-d9.
BEFORE opportunity afc5143e held version 1 with is_current FALSE, so there was no current version and every
future amendment for it was permanently unrecordable.
AFTER version 2 landed alongside the retired version 1. The invariant is back to exactly ONE current
version of two total, and the run reported "wrote 1 new opportunity versions and raised 1 alerts"
with Result=success and no amendment failures, so the new fail last assertion did not need to fire.
THE STRANDED ROW NEEDED NO DECISION AFTER ALL. GATE_E had listed it as awaiting Joseph's call; the correct
numbering simply resolved it on the first run, which is what a self perpetuating bug looks like once the
thing perpetuating it is gone.
GATE C FIXTURE ACCOUNTS, recorded because they persist. gatec.recruiter@fg-talent.test and
gatec.manager@fg-talent.test exist on STAGING ONLY, in the Package B organization, holding slateMATCH, seeded
as the protocol's "seeded fixture accounts per role". Their passwords were set through cmd/rotate-credential so
they go through the real pepper and PBKDF2 path, and the values are NOT recorded here or anywhere in the
repository, per finding D4. Re set them with rotate-credential when the next sweep needs them.
## THREE MORE FINDINGS, ALL FROM THE LAST HOUR, TWO OF THEM MINE | 29 July 2026
FINDING D12, ONE 97 KB DOCUMENT KILLED THE ENTIRE EXTRACTION BATCH. 07_TE6_Deliverables.pdf from Package B is
97,642 bytes and six pages, and opportunity-extract was OOM KILLED on it, exit 137. It printed no verdict for
the document and never reached its own DONE line, so one small file took the whole run down.
THAT CONTRADICTED THE PROGRAM'S OWN PROMISE, written at the top of its main file: a document it cannot read is
marked failed WITH A REASON while the run continues. Being killed by the kernel is not a reason.
DIAGNOSED RATHER THAN GUESSED. A page by page diagnostic showed NewReader returning cleanly and reporting six
pages, then the process dying inside the FIRST page's GetPlainText before any text came back. So the runaway
allocation is inside ONE library call, which rules out every in process fix: a page bound does not help when
page one is the problem, an output bound does not help when the allocation is inside the callee, and the
existing panic recovery does not help because a process the OOM killer takes never runs a deferred function.
FIXED BY ISOLATION. The library now runs in a CHILD process, the extractor re executing itself in a worker
mode that reads bytes on stdin and writes text on stdout, under a 90 second deadline, an output cap, and on
Linux a 1 GiB address space limit the child sets on itself. A dead child is a fact about one file, exactly as
a panic already was, and the parent falls through to the hand rolled reader. It re executes ITSELF so there is
no new dependency and the no Python rule is untouched.
WHY THE LOCAL CAP MATTERS BEYOND THIS FILE: the OOM killer does not necessarily kill the process that asked
for the memory. On staging it chose the extractor. On a busier host it could have chosen the API.
PROVEN: the batch now reports "ok=1 failed=1" with the document named no_text_layer, and a regression test
guards it, skipped unless the fixture is present because a 97 KB binary does not belong in the repository.
ALSO FIXED WHILE IN THERE: inflateStreams bounded each stream to 32 MB and had NO ceiling on the SUM, while
its own comment claimed a decompression bomb was ruled out. It was not. There is now an aggregate budget, and
exceeding it REFUSES the document rather than returning truncated text that looks complete.
FINDING D13, MY OWN D11 FIX BROKE THE NIGHTLY BACKUP, and the alerting caught it. Transferring ownership of
every staging object to missionmatch_staging_migrator removed missionmatch_migrator's implicit owner
privileges, and the backup dumps STAGING using that role. pg_dump failed with "permission denied for table
goose_db_version" and the unit failed.
THE OnFailure WIRING FROM D2 PAID OFF ON A REGRESSION I CAUSED. The failure raised mail rather than sitting in
a journal, which is exactly what that work was for.
FIXED WITH READ ACCESS, NOT OWNERSHIP BACK: SELECT and schema USAGE on both schemas, plus DEFAULT PRIVILEGES
so the next migration does not reintroduce it on a night nobody is watching. Ownership stays with the staging
migrator, which is what lets staging DDL run at all.
IT TOOK TWO PASSES AND THE SECOND ONE IS THE LESSON. The first grant covered every TABLE in both schemas and
sequences in public only, so pg_dump got past all 88 tables and then failed on candidate_demographics_id_seq
in the eeo schema. A partial grant moves the error later without removing it.
DIRECTION MATTERS AND IS WHY THIS IS SAFE: D6's isolation exists to stop STAGING reaching PRODUCTION, and this
is the opposite direction. Asserted afterwards: missionmatch_staging_migrator still has CONNECT on the
production database REVOKED.
BACKUP RESTORED AND PROVEN: Result=success, 11 artifacts, offsite OK 11 of 11 verified by checksum on
VALKYRIE. Restore drill rerun: PASSED, 11 seconds for 16 MB, schema v84, all ten reconciliation rows OK.
FINDING D14, A LIVE CREDENTIAL WAS DISCLOSED INTO A LOG, BY ME, AND THIRTEEN BINARIES COULD HAVE DONE THE SAME.
WHAT HAPPENED. I added the PDF worker mode but checked its hidden flag AFTER flag.Parse. The flag package saw
an unrecognised flag, printed its usage, and USAGE PRINTS DEFAULTS. The DSN was a flag default read from the
environment, so the live missionmatch_staging_migrator password was written to stdout and into the staging
journal. One unrecognised flag, one disclosed password.
THE SYSTEMIC HALF IS WORSE THAN MY MISTAKE. A sweep found SEVENTEEN flag defaults across THIRTEEN binaries
reading secrets from the environment, including cmd/rotate-credential, whose usage output would have printed
the CREDENTIAL PEPPER, the server side key applied before PBKDF2. Every one of them disclosed a live secret on
any usage error, any typo, any unknown flag. This was latent long before today and my mistake only triggered it.
FIXED IN ALL THIRTEEN. No secret is a flag default any more: the flag defaults to empty, the help text NAMES
the environment variable instead of showing its value, and the value is read after parsing. PROVEN with a
planted secret in six environment variables: 0 of 13 binaries disclose it, and the check was itself controlled
by reverting one binary, watching the leak reappear, and restoring it.
ROTATED, BECAUSE A DISCLOSED CREDENTIAL THAT IS MERELY REDACTED IS STILL DISCLOSED. Followed the documented
procedure in order: fresh backup, update the one consumer FIRST, ALTER ROLE LAST, restart what reads its DSN at
boot, prove by role name and status code.
PROVEN OLD DEAD AND NEW LIVE: the disclosed value is now refused with "password authentication failed", the
consumer authenticates as missionmatch_staging_migrator on missionmatch_staging, staging healthz 200, and
CONNECT on production is still revoked. Integration tests re run green against the rotated credential.
SCOPE, STATED HONESTLY: this role reaches missionmatch_staging ONLY, and that was already proven by refusal
during D6, so the exposure was staging and not production. The value also remains in the staging systemd
journal until it rotates out, which cannot be scrubbed and is recorded rather than glossed.
I ALSO BLAMED THE ROTATION FOR SOMETHING IT DID NOT CAUSE, AND THE CORRECTION IS D16. A 30 minute soak was in
flight when the rotation landed and it ran ten minutes past its deadline still burning CPU, so I attributed
that to the rotation breaking its connections. A SECOND soak, started afterwards on the correct credential
from the beginning, did exactly the same thing. The rotation was innocent. See D16.
FINDING D16, THE STRESS TOOL CANNOT REPORT A SOAK OF THE LENGTH THE PROTOCOL ASKS FOR, AND NEVER COULD | 29 July
2026. Two long soaks were lost before this was diagnosed, and the first loss was wrongly blamed on the D14
credential rotation happening at the same moment.
THE CAUSE, found by elimination rather than by guessing again. pg_stat_activity showed NO active or blocked
queries on staging, so the workers were not waiting on the database. The CPU was being burned AFTER the load
finished, in the reporting step, and percentile() used an INSERTION SORT:
// Sorting a few thousand durations is not the bottleneck here and a simple implementation is easier to
// trust than a clever one in a measuring instrument.
The reasoning was sound and the premise was false. Thirty minutes at roughly a thousand reads per second is
about 1.8 MILLION samples, not a few thousand, and insertion sort is O(n squared): on the order of a trillion
operations, run TWICE for p95 and p99.
WHY NOBODY SAW IT. The first Gate B bundle ran a SIXTY SECOND soak, which is 60,000 samples and completes in
seconds. The protocol asks for THIRTY MINUTES. So the protocol's soak requirement has never once been
measurable with this instrument, and the gap was invisible because the only soak ever run was 30 times shorter
than the one required.
A MEASURING INSTRUMENT THAT CANNOT REPORT THE MEASUREMENT THE PROTOCOL ASKS FOR IS NOT A SLOW INSTRUMENT, IT
IS A MISSING ONE. Every number was collected correctly and none of it was printable.
FIXED with sort.Slice. Proven: a 40 second soak now reports 45,335 samples at p95 8.756ms instantly, and the
full 30 minute run was started afterwards.
THE LESSON WORTH KEEPING: a comment justifying a simplification is a claim about the input, and that claim
needs checking against the input the protocol actually specifies rather than against the input the author had
in mind.
FINDING D15, THE UPTIME WATCH RAISED A FALSE ALARM AND THE ALARM TEXT HID THE REASON | 29 July 2026, found
during the final state check because mm-watch showed as a failed unit.
THE ALERT SAID: "PUBLIC SITE: https://missionmatch.thatwebhostingguy.com/ returned 200000, expected 200". The
site was answering 200 the entire time.
THE BUG IS ONE SHELL IDIOM:
code() { curl -s -o /dev/null -w "%{http_code}" --max-time "${2:-15}" "$1" 2>/dev/null || echo 000; }
When curl PRINTS a status code and THEN exits nonzero, which it does on a timeout or a partial transfer after
the headers have already arrived, BOTH outputs land: the real code from -w and the 000 from the fallback. The
caller compares "200000" to "200", it does not match, and the watch reports an outage that is not happening.
WHY IT MATTERS MORE THAN ONE FALSE ALERT. A monitor that cries wolf is a monitor that gets ignored, and this
ledger has written that sentence about other instruments more than once. It is also SELF CONCEALING: the alert
text is unreadable, so the natural reaction is to distrust the message rather than to suspect the checker.
FIXED: the helper accepts exactly three digits from curl and substitutes 000 otherwise, so the two outputs can
never concatenate. Previous version kept at /root/mm-watch.sh.pre-d15.
PROVEN IN BOTH DIRECTIONS. Real run: OK, exit 0. Control, pointed at a hostname that cannot serve the app:
"PUBLIC SITE: ... returned 000, expected 200", exit 1. The message is now readable and the detector still
detects, which is the pair of properties that were both broken.
## INTEGRITY CHECK, GATE B CLOSURE, AND TWO MORE OF MY OWN REGRESSIONS | 29 July 2026, evening
INTEGRITY CHECK AFTER A SERVER ERROR, AND THE HONEST ANSWER IS THAT NOTHING WAS DAMAGED. A server error
interrupted a Gate B evidence update mid write, so every evidence and ledger file touched in that window was re
read and verified rather than assumed intact.
CHECKED: ten files. All ten end with a newline and a sensible final character. Zero duplicate section headings
where exactly one is expected, checked for the handoff, the reconstruction entry and the scope completion
entry. Working tree CLEAN at fff3069, so nothing half written was sitting uncommitted either.
THE FINDING IS A GAP, NOT A CORRUPTION. gate-b-results-2.txt was intact at 87 lines and contained ZERO
occurrences of the http-load or D16 content it was about to receive: the interrupted write never began. So
there was nothing to repair and something genuine to finish. The source numbers all survived in the scratch
logs and the file was completed from them rather than from memory.
RECORDED BECAUSE A CLEAN INTEGRITY CHECK IS STILL A RESULT. The instinct after an interruption is to assume
damage and start rewriting, and rewriting a file that was fine is its own way of losing information.
THE PRODUCTION AUTHENTICATED LEG IS DONE, closing the gap the reconstruction entry named and the standing order
approved. Method of record followed exactly: a dedicated probe provisioned, used, and retired.
PROVISIONED phase2.probe@missionmatch.test in the CONTROL PLANE, role viewer, NO tool grants, lowest
privilege. Password set through cmd/rotate-credential so it went through the real pepper and PBKDF2 path
rather than a hand written hash.
LOGGED IN THROUGH THE PUBLIC PRODUCTION URL: login 200, session 200.
BUILD ROUTES DARK ON THE AUTHENTICATED LEG, four samples each so both pool members answer:
approve 404 404 404 404, scope 404 404 404 404, tracked 405 405 405 405
AND THE FIVE NEW PHASE 2 ROUTES ARE DARK BEHIND A VALID SESSION TOO, which is the point of checking them:
split 404, merge 404, generate 404, amendments 404, draft PATCH 404
ZERO 5xx ON EITHER NODE during the window, checked per node rather than inferred: Bubbles 0, megamind-agi 0.
The nginx log carries 25 5xx TOTAL across its whole history, every one from 06:40 this morning on the STAGING
vhost, which are the known F3 upload failures. ZERO in the window. My first count of 44 was a bad grep and is
corrected here rather than left in the record.
RETIRED, never deleted: status disabled, sessions revoked through the product's own
auth_revoke_user_sessions, 0 live sessions with the row and its 1 historical session preserved. PROVEN
retired: a fresh login returns 401 and the old session cookie returns 403.
GATE B IS COMPLETE. Every Phase 2 stress item and every general Gate B item has now been executed with a
control that failed first. Evidence at docs/evidence/phase2/stress/gate-b-results-2.txt.
THE 30 MINUTE SOAK REPORTED FOR THE FIRST TIME: 2,028,801 samples, rps 1127, p95 8.931ms, p99 10.503ms, zero
errors, against a 300ms target. Two million samples is itself the proof of D16.
AUTHENTICATED HTTP LOAD, open across TWO phase closes, now closed: 25,924 samples over 10 minutes paced to the
protocol's 50 rps, p95 37.352ms, p99 67.644ms, ONE HUNDRED PERCENT SERVED, zero 429, zero 5xx.
THE FAILURE DRILL IS COMPLETE IN ALL THREE PARTS. Failover was measured for real by D6. Saturation produced
17,314 of 429 and ZERO of 500. And the worker kill drill ran: five documents pending, SIGKILL 1.2 seconds in
having decided 3 of 5, rerun completed the other 2, all five done, zero pending, and ZERO chunks sharing a
document, a content hash and a section label. The one same hash pair that exists is legitimate, the literal
text "Warranty Program. N/A" appearing twice in the source.
FINDING D17, I DELETED flag.Parse AND DEPLOYED THE RESULT. While removing a duplicated worker check block from
cmd/opportunity-extract, my edit took the flag.Parse call and both environment fallbacks with it. Every flag
stayed at its zero value, so the binary fatalled on "-dsn or MM_MIGRATOR_DATABASE_URL is required" on a host
where that variable was plainly set. It was DEPLOYED to staging in that state.
IT FAILED LOUDLY, WHICH IS THE ONLY REASON IT WAS CAUGHT IN MINUTES. A binary that had instead defaulted to
something plausible would have run against the wrong target silently. Loud failure is a feature.
A CHECK THAT WOULD HAVE CAUGHT IT NOW EXISTS AND WAS RUN ACROSS THE WHOLE TREE: every binary declaring flags
must call flag.Parse. Result: 0 binaries declaring flags without parsing them, so this was the only one.
FINDING D18, MY D12 FIX SILENTLY DEGRADED EVERY PDF, WHICH IS WORSE THAN THE BUG IT FIXED. The isolated PDF
worker was given RLIMIT_AS of 1 GiB, reasoning that a local cap makes a runaway fail on the process that caused
it. The reasoning holds; the implementation did not. RLIMIT_AS bounds VIRTUAL address space and the Go runtime
reserves large virtual arenas at startup, so EVERY worker died instantly with "fatal error: runtime: out of
memory" before reading a byte.
EVERY WORKER FAILURE FALLS BACK TO THE WEAKER HAND ROLLED READER, which is right for an unreadable document
and wrong for a readable one. So a fix aimed at ONE pathological file quietly degraded extraction for ALL
files: 04_PWS_original went from 55 chunks to 3 and REPORTED SUCCESS while doing it. That is a green result
over a quietly worse outcome, the exact shape this build keeps finding, and I introduced it.
CAUGHT BY COMPARING AGAINST A RECORDED BASELINE rather than by reading the log, which said ok=1 and looked
fine. The number 3 where 55 was expected is the only reason it surfaced.
FIXED by removing the rlimit entirely. THE HARD BOUND WAS ALWAYS THE ISOLATION, not the limit: the library
runs in a child, a runaway kills the child, the parent names the document and the batch continues. GOMEMLIMIT
is set as a SOFT hint and is described as soft rather than sold as a cap. Pdeathsig still ties the child's life
to the parent's.
PROVEN BOTH WAYS AFTER THE FIX. 04_PWS_original is back to 55 chunks, now including a recovered table. And the
crasher is STILL contained: "the isolated reader failed (signal: killed), falling back", document named
no_text_layer, batch DONE ok=0 failed=1. The kernel killed the CHILD and the parent survived, which is
precisely the property D12 required and it needs no rlimit to hold.
THE TABLE READER IS ALSO VISIBLY EARNING ITS PLACE now that the worker path works: the amended PWS recovered
91 tables and 486 rate rows, going from 300 chunks to 391.
D16 IS CLOSED. The percentile function is O(n log n), the 30 minute soak has been run and reported, and the
lesson is codified as a standing Gate D rule plus a new file, docs/POSITIVE_CONTROL_LIBRARY.md, which gives the
library the protocol has described since 28 July an actual home. Seventeen controls are listed, each naming the
instrument, the lie it told, and the control that catches it.
THE WRONGLY BLAMED CORRECTION, NAMED: D16's first lost soak was attributed to the D14 CREDENTIAL ROTATION
landing at the same moment. D14 was INNOCENT. A second soak, started afterwards on the correct credential from
the beginning, hung identically, which is what proved it. That wrong attribution cost a second lost run,
because a wrong answer closes a question that should have stayed open. When two things change together, prove
which one did it.
## THE FOUR FINDINGS HOLDING PHASE 2 OPEN, 29 July 2026
RESOLUTION, WRITTEN 29 JULY 2026 LATE, on Joseph's recorded decisions. THREE OF THE FOUR ARE CLOSED. The
fourth executed and is blocked at the far end by something no amount of work here can reach. Each original
block is left standing below with its status line updated, because the reasoning that produced a finding is
worth more than a tidy list.
1 CLOSED BY DECISION. Scanned or undecodable text layers are a graceful terminal state with a clear
no_text_layer status surfaced in the UI. OCR is not in this build and is recorded
as future candidate P2 in docs/IMPROVEMENTS.md. The trial evidence shows the wage
determination carrying that status correctly.
2 CLOSED BY DECISION. Office formats are stored, hashed and versioned with a graceful unsupported_format
status for Phase 2. The reader itself, an xlsx library bake off measured against
the real Package A spreadsheet plus a minimal docx text path, is PRE APPROVED as
Phase 3's single improvement item. Phase 3's slot is now spent.
3 NOT CLOSED, EXECUTED. The full bounded run was performed. Every mechanism on this side executed against
the live external endpoint for the first time, and all three calls were refused by
OpenAI with 429 insufficient_quota. The key is VALID; the account has no credit.
Evidence docs/evidence/phase2/findings/finding-3-external-normalize-path.txt.
NEEDS ONE THING FROM JOSEPH: credit on that account. Then three calls and ninety
seconds. Acceptance fixed in advance: three outcome rows reading openai:gpt-4o-mini.
4 CLOSED BY EXECUTION. The sweep now covers 15 enumerated out of repo scopes on two hosts, with 13
positive controls, both directions, tracked and untracked, inside and outside the
repository. It found two complete DSNs in mm-identity-sync.sh on its first working
run, which CREDENTIAL_CONSUMERS.md had described as a trap and left standing.
Repaired, and the same change took both credentials out of argv.
Evidence docs/evidence/phase2/findings/finding-4-secret-sweep-out-of-repo.txt.
Every other finding raised in this build is closed. These four are not, and each one is stated with its
status, the evidence that establishes it, and what closure actually requires. None of them is a correctness
defect: three are capability limits and one is an instrument gap.
OPEN 1, THE SCA WAGE DETERMINATION IS NOT EXTRACTABLE BY THIS BUILD
STATUS CLOSED BY DECISION, 29 July, Joseph. A scanned or undecodable text layer is a GRACEFUL TERMINAL
STATE: stored, hashed, versioned, status no_text_layer surfaced in the UI with both causes named,
and ZERO chunks so no reviewer can cite noise. OCR is not in this build; recorded as candidate P2
in docs/IMPROVEMENTS.md. Was an open capability limit. NOT a regression, and the pipeline is now HONEST about it where before it
was not: the document reports extraction FAILED with no_text_layer and stores ZERO chunks, where
previously it reported DONE with one stored chunk of character soup.
EVIDENCE docs/evidence/phase2/functional/gate-a-phase2-close.txt. Measured on staging: 231 of 231 spans
refused, 0 chunks. The PDF's text layer decodes to mojibake because its embedded font carries no
usable character map, which is a DECODING limit and not a table extraction limit. Table extraction
itself is proven by 7 unit tests on a real shaped wage determination and by the volume profile
parsing a 200 page PWS into 400 chunks, 200 tables and 1600 rate rows.
CLOSURE A character map aware PDF reader, so the font's ToUnicode CMap is applied before text is scanned.
That is a distinct capability and belongs in its own phase item, not in a Phase 2 patch. Until
then the wage determination's rate grid, the legal floor under every bill rate in a proposal, is
not available to the product and the product says so.
OPEN 2, NO OFFICE FORMAT READER
STATUS CLOSED BY DECISION, 29 July, Joseph. For Phase 2 office formats are stored, hashed and versioned
with a graceful unsupported_format status and nothing is invented from them. The reader itself,
xlsx through a MEASURED Go library bake off with the real Package A spreadsheet as fixture plus a
minimal docx text path, is PRE APPROVED as Phase 3 single improvement item. Phase 3 slot spent.
Was an open capability limit, and arguably the allowlist working rather than a defect.
EVIDENCE Staging: 05_PWS_Att4_Course_Schedule.xlsx reports FAILED unsupported_format with 0 chunks. The
refusal is BY NAME and by a closed vocabulary, which is the designed behaviour for a format this
build cannot read honestly, rather than a decoder producing plausible output from a format it does
not understand.
CLOSURE Either an office reader for xlsx and docx, or a recorded DECISION that spreadsheet attachments are
out of scope and will be refused permanently. Both are legitimate; leaving it undecided is not,
because a course schedule is real scope a reviewer may need.
OPEN 3, THE EXTERNAL REQUIREMENT_NORMALIZE PATH HAS NEVER EXECUTED AGAINST OPENAI
STATUS EXECUTED 29 July on Joseph's order, STILL OPEN, blocked at the far end. The bounded run happened in
full: the boot guard was controlled both ways, the backend was enabled on a temporary process so no
persistent gateway was touched, three public notices went through the gate, three egress rows and
three outcome rows were written, and the flag is verified off again. OpenAI refused all three with
429 insufficient_quota. The key is VALID, an invalid key answers 401; the ACCOUNT has no credit.
The gateway fell back to the local runtime, which the outcome rows caught and named, and that catch
is the reason this is recorded as unproven rather than closed. What remains unproven is precisely
one thing: the verifier gating prose written by an external model.
CLOSING IT NEEDS CREDIT ON THAT ACCOUNT and nothing else. Note from the 28 July decision: this key
was pasted into chat and sat in a plaintext Desktop file, so rotating before funding is the safer
order. Evidence findings/finding-3-external-normalize-path.txt.
EVIDENCE The backend, the boot guard, the egress gate and the audit are built and proven, and the scrub
gate permits only public SAM notice text under that class, with the candidate row and tenant
document fixtures refused from anywhere in a payload. What has never run is the gateway actually
calling OpenAI: its generation backend is config selected and points at the LOCAL model.
CLOSURE Joseph's decision, then one end to end run. It is a gateway CONFIG change protected by the boot
guard, not new code. I did not enable it because it sends data to a third party and carries cost,
which makes it a decision with policy attached rather than a verification step I can take on my
own authority.
OPEN 4, THE SECRET SWEEP CANNOT SEE OUTSIDE THE REPOSITORY
STATUS CLOSED BY EXECUTION, 29 July. 15 enumerated scopes on two hosts, 13 positive controls passing in
both directions, and a counted run reporting 15 of 15 scopes answered and clean. It found what it
was built to find on its first working run: two complete DSNs, passwords included, in
mm-identity-sync.sh, which a timer runs hourly as root and which this very inventory had already
called a trap and left standing. Repaired to resolve both from db.env by name, and the same change
took both credentials out of argv, measured 1 exposure before and 0 after.
TWO DEFECTS IN THE INSTRUMENT WERE FOUND ON THE WAY, both of the house failure mode: ssh inherited
the scope file on stdin and drank 12 of 13 lines so ONE scope ran and the sweep said clean, and the
guard refused only on zero scopes so one out of thirteen satisfied it. It now counts expected
scopes from the file. At least one answered is not a check.
EVIDENCE docs/CREDENTIAL_CONSUMERS.md now carries an ops scripts section, added because TWO of the five
consumers broken by the D5 rotation lived in /usr/local/sbin and no inventory listed them. One of
them, mm-restore-drill.sh, held a migrator password in PLAINTEXT and the sweep could never have
found it, because the sweep scans the repository and that file is not in it. D14 then showed the
same class systemically: seventeen flag defaults across thirteen binaries would disclose a live
secret on any usage error.
CLOSURE Extend tools/verify/secret-sweep to accept named paths outside the repository, run it against the
ops script directories on both hosts, and give it a positive control planted in one of those paths.
Until then the sweep's clean result means "clean in the repository", which is narrower than it
reads, and that narrowness is now written down rather than assumed.
NOT ON THIS LIST, BECAUSE THEY ARE NOT FINDINGS
GATES A, C AND D NEED RE RUNNING. All three passed, and then code changed after they passed: the readability
bar, the process isolated PDF worker, the pairing fix, the thirteen binary flag change. Their PASS verdicts
describe a commit that is no longer HEAD. That is ordinary sequencing, not a defect, and the re runs are the
first work of the next session.
PIXEL BASELINES await Joseph's approval of the contact sheet. That is Gate E working as written.
## Phase 0, security and preflight
phase0 | item0 execution design spec written | docs/superpowers/specs/2026-07-27-opportunity-to-outcome-execution-design.md present, approved by Joseph in chat | 7641374 | 2026-07-27
phase0 | item5 archive move | docs/archive/INTELLIGENCE_EXPANSION_SPEC.md present (228 lines), governing directive added as docs/BUILD_DIRECTIVE.md | e662665 | 2026-07-27
phase0 | item6 BUILD_STATE ledger created | this file exists with the section 0 format | 2595999 | 2026-07-27
phase0 | amendment staging environment rule | directive Section 0 item 9 added, directive renamed to docs/CLAUDE_CODE_BUILD_DIRECTIVE.md, through Phase 5 all DB work runs on missionmatch_staging megamind-agi 5433, production M4 only at Gate 6 | this commit | 2026-07-27
phase0 | item1 human OpenAI key revoked | Joseph confirms revocation in the OpenAI dashboard | human step | 2026-07-27
phase0 | item2 human project scoped key placed | key on M4 as openai_api_key root 0600, read only via the secrets store, enabled flag false | human step | 2026-07-27
phase0 | item3 human SAM.gov system account applied | application submitted today, revisit at Phase 4 | human step | 2026-07-27
phase0 | item4 scrub egress classes and external call audit gate | go test scrub and ai green; AllowEgress fails closed on an unknown class and on a uuid, email, or phone marker; AllowExternal records backend, class, byte count, and content hash, never content | 8ba3318 | 2026-07-27
phase0 | openai gateway backend and boot guard | generateOpenAI added over the chat completions contract, key read only through the secret store, enabled flag default false; go test cmd/megamind-gateway green | 110bd1c | 2026-07-27
phase0 | GATE 0 passed | built the real gateway binary and ran it: openai enabled with no key exits 1 (openai_api_key is [credential removed] from the secret store, refusing to boot), openai enabled with an empty model exits 1; scrub and egress tests pass; BUILD_STATE exists; archive move committed at e662665; make ci-gates PASS on backdoor and FIPS | 110bd1c | 2026-07-27
## Conflicts recorded (directive Section 0 items 6 and 9)
CONFLICT phase1 staging location and parity | The amendment names staging as missionmatch_staging on megamind-agi port 5433. Ground truth from the live /etc/missionmatch/staging.env, used by the running mm-api-staging service on 127.0.0.1:8097, is that staging is missionmatch_staging on M4 100.94.8.94 port 5432, a DISTINCT database on the SAME Postgres server as the production missionmatch database. Staging runs shared tenancy, production runs per_tenant. Staging goose version is 59, repository head is 0068, so staging is nine migrations behind parity, and the opportunity tables do not yet exist there. Stopped before any write per Section 0 items 6 and 9. Awaiting Joseph confirmation that the correct Phase 1 target is missionmatch_staging on M4 5432, that bringing it from 59 to 68 to reach parity is authorized, and that targeting only the missionmatch_staging database, never missionmatch, satisfies the do not touch production before Gate 6 rule. | pending | 2026-07-27
RESOLVED phase1 staging conflict | Joseph decided there is no separate staging: the build extends the existing application directly and verifies against the live database with a connected database name assertion and a pg_dump snapshot before each write. missionmatch_staging is not used. Directive Section 0 item 9 updated to supersede the staging amendment. | beab171 | 2026-07-27
## Phase 1, opportunity foundations
phase1 | item1 ingest_manifest migration | 0069 applied to production missionmatch on M4 5432 (goose 68 to 69), shared reference data with no RLS (rls_enabled false), columns id, source, url, license, fetched_at, record_count, content_hash, created_at, app grants SELECT and INSERT, schema snapshot saved on Bubbles at /home/user/mm-snapshots; the down refuses to drop when provenance rows are present | a77dd65 | 2026-07-27
phase1 | item1 tracked_opportunities migration | 0070 applied to production (goose 69 to 70), tenant table verified rls_enabled and rls_forced true, 1 isolation policy, 4 foreign keys (organizations, ingest_manifest, users twice via the column list SET NULL form), indexes idx_tracked_opps_org_status and the unique partial uq_tracked_opps_org_public and uq_tracked_opps_org_id, app grants SELECT INSERT UPDATE no DELETE, 31 columns per Section 3.1; snapshot taken first; down refuses when rows present | cd7ca09 | 2026-07-27
phase1 | item1 opportunity_versions migration | 0071 applied, immutable snapshot table, rls_forced, 15 columns, 5 FKs, trigger opportunity_versions_immutable_trg allows only the is_current flag and blocks delete, unique (org, opportunity_id, version_number), grants SELECT INSERT UPDATE | 2026-07-27
phase1 | item1 opportunity_documents migration | 0072 applied, rls_forced, 18 columns, 5 FKs, document_type and extraction_status enum CHECKs, sha256 duplicate partial unique index, grants SELECT INSERT UPDATE | 2026-07-27
phase1 | item1 opportunity_document_chunks migration | 0073 applied, rls_forced, 10 columns, FK to opportunity_documents on delete cascade, extraction provenance, grants SELECT INSERT | 2026-07-27
phase1 | migrations applied via migrate-all-tenants | the sanctioned tool (goose library, embedded migrations, session locked, idempotent), 1 target control plane missionmatch on M4 5432 with 0 active tenants, public v70 to v73, eeo v2 unchanged; the goose CLI is avoided because it pulls heavy unrelated drivers not in go.sum | 2026-07-27
phase1 | opportunity capture API and screen | capability opportunity.manage added at recruiter and above (matrix test green); opportunities module gains the tenant capture layer (CreateTracked, ListTracked, GetTracked, PatchTracked via RunInTenantTx, org from GUC, audited), mounted under the slateMATCH tool gate at /opportunities/tracked (Section 9 path resolved around the public cache owning /opportunities/{noticeID}); chi.Walk route contract test green; Capture intake screen at /slate/capture | 13d6399 and 7a196ca | 2026-07-27
phase1 | opportunity capture deployed and verified live | new FIPS binary deployed to Bubbles (staged sha256 swap, healthy in 2s), frontend assets swapped with the 5 SEO lines intact; through the public URL the site is 200, the new bundle serves, /slate/capture serves the SPA 200, and POST /api/opportunities/tracked returns 403 (route live, auth required) consistently with no stale replica 405 | 2026-07-27
OPEN phase1 | megamind-agi replica out of the pool | the mm-api replica at 100.77.114.40:8094 has no direct SSH (port 22 refused, SSH lands on the Windows host cmd with WSL nested), so it could not receive the new binary; it is marked down in /etc/nginx/conf.d/mm-api-upstream.conf so all traffic reaches the updated Bubbles node. FOLLOW UP: establish a WSL deploy path for megamind-agi, redeploy the binary, and remove the down flag to restore two node failover | 2026-07-27
RESOLVED phase1 | megamind-agi replica back in the pool | Joseph provided access (josep@100.69.120.61). The replica mm-api runs in WSL Ubuntu as a system service (User mm-api), binary /opt/missionmatch/mm-api. Deployed the new binary through the Windows /mnt/c bridge (sha 8647587c matches Bubbles), restarted, healthy in 2s, POST /api/opportunities/tracked returns 403 not the stale 405. Removed the down flag, reloaded nginx; both nodes serve consistently (8 of 8 403 through the public URL, and each node directly). Two node failover restored. WSL deploy path for future releases: scp the binary to the Windows host home, then run a bash script via wsl that stops the service, copies from /mnt/c to /opt/missionmatch, and restarts; sudo in WSL is NOPASSWD | 2026-07-27
phase1 | item2 intake screen 2 opportunity intelligence | /slate/capture/:id detail and edit view built and deployed (frontend only, backend PATCH already live on both nodes); edits agency, office, solicitation, notice type, NAICS, PSC, set aside, place of performance, incumbent, source URL, deadline, prime or sub, status; public verify site 200, new bundle 200, detail route serves the SPA 200; tsc and vite clean | 4defe49 | 2026-07-27
phase1 | opportunity embedding layer, the AI learns the opportunity landscape | migration 0074 applied to production (goose 73 to 74): opportunity_records gains embedding vector(384) with a partial HNSW cosine index (the skills pattern, global data outside RLS) and a GENERATED search_tsv with a gin index. embed-backfill extended with -what opportunities (existing binary extended, no new binary). Ran against production: 6535 of 6535 embedded, exit 0, model qllama/bge-small-en-v1.5. SEMANTIC RETRIEVAL VERIFIED LIVE: the natural language query "cybersecurity engineering support for the Air Force" returns PACAF Cyber Systems Operations Support, Cybersecurity Support Services, and Cyber Security Services at 0.78 cosine, ranked by meaning not keywords. Before this the catalog had no vectors at all and only skills, candidates, and ai_memory were embedded | 084e521 | 2026-07-27
## Amendment 6, accepted 28 July 2026
AMENDMENT 6 ACCEPTED | source docs/DIRECTIVE_AMENDMENT_6.md (224 lines), triggered by live FG Talent design
partner feedback. Merged into the directive: acceptance items 36 through 40 appended to Section 11, and
PHASE 5A, Identity and Access, inserted between Phase 5 and Phase 6 with its own Gate 5A. Creates two
containers, TRACK U (urgent, production eligible under the Section 2 item 8 guardrails) and Phase 5A. Role
enum values are extended, never renamed or removed. The scoring engine stays frozen. | 2026-07-28
TRACK U ITEM 1, ROOT CAUSE RECORDED BEFORE ANY FIX, as the amendment requires | Investigated the three
reported symptoms against the code and the staging database.
SYMPTOM 1, a client superuser could not edit her own name. ROOT CAUSE, conclusive: full_name appears ZERO
times in internal/modules/auth/handler.go, service.go, and helpers.go. It exists only in dto.go, on the
invite and user list shapes. There is no self service profile endpoint anywhere in the API, and there is no
endpoint that mutates a name for ANY actor. The only user mutations that exist are role, tools, status,
mfa reset, and recovery reissue. A name is therefore written once at invite acceptance and is IMMUTABLE
thereafter for every role including admin, owner, and superuser. This was never a permissions problem: the
capability does not exist, which is why escalating to the top of the ladder did not help her.
SYMPTOM 2, the Superuser tab rendered inactive. ROOT CAUSE: there is no separate Superuser tab component;
the word appears only in explanatory copy on the Admin screen. What renders inactive is the tool entitlement
grid at frontend/src/screens/Admin.tsx:308, const locked = !mine && !on, which greys out any product tool
the ACTOR does not personally hold, because an actor may only confer a tool they already have. A client top
seat who was not granted every product tool therefore sees permanently greyed controls that read as broken
rather than as not applicable. The deeper cause is the one the amendment names: superuser conflates the
platform plane with the tenant top seat, so a client is shown platform shaped controls at all.
SYMPTOM 3, no tenant facing user management. PARTIALLY PRESENT, not absent: GET /users exists gated on
RequireRole(RoleAdmin), which uses AtLeast, so a superuser at rank 6 does reach it, and the Admin screen is
routed at /admin with a /people alias. What is genuinely MISSING is name editing (no endpoint at all, per
symptom 1), self service anything, and a tenant scoped management surface that does not also expose
platform concepts.
STAGING FACTS for the fix: the users table column is full_name, NOT display_name, so the amendment's display
name wording maps onto full_name rather than a new column. Staging currently holds NO superuser (roles
present: owner 163, recruiter 881, manager 16, member 13), so reproducing as the reporting user's role
requires provisioning a superuser on staging first. The users role CHECK constraint allows exactly
superuser, owner, admin, manager, recruiter, member, viewer, with NO operator value, so Track U item 4
requires a migration that EXTENDS the constraint, never renames or removes a value. | 2026-07-28
DECISIONS 12 AND 13 RATIFIED AND FOLDED INTO AMENDMENT 6 | Joseph, 28 July 2026.
DECISION 12, BY RULE EXTENDS TO TOOLS: a tenant superuser holds every product tool its tenant is entitled
to, by rule not by grant, exactly as it holds every capability by rule within its tenant; operator holds all
tools in every tenant; so the entitlement grid can never render the top seat as broken. Confer only what you
hold stands unchanged for every role BELOW superuser, where it is load bearing.
DECISION 13, NO SILENT GREY: a disabled entitlement control must state its reason, and the two reasons are
distinct, the organization is not entitled to that product versus you do not personally hold that tool. A
bare grey control reads as a broken product, which is exactly how this defect reached Joseph.
ADAPTATIONS CONFIRMED: full_name is the column and the amendment's display name wording maps onto it, no new
column; the users role CHECK is EXTENDED by migration to admit operator, never renamed, never narrowed. |
2026-07-28
THE REPORTING USER'S PRODUCTION ACCOUNT, read only lookup, recorded as the repro baseline | Production holds
ONE organization, Acme GovCon (11111111-1111-1111-1111-111111111111), and three users, ALL of them superuser:
joseph.w.anady@gmail.com (active, Joseph Anady, mfa false, tools missionmatch, recruitmatch, screenmatch,
slatematch), tallen282005@gmail.com (active, Tallen, mfa false, same four tools), and the reporting user
patriciacox6@aol.com (Patricia Cox, mfa false).
PATRICIA'S ACCOUNT IS THE BUG IN ONE ROW: role superuser, status INVITED, last_login NEVER, and tool grants
(NO TOOLS GRANTED). The other two superusers hold all four tools explicitly; she holds none. Under the old
confer only what you hold rule, an actor holding zero tools sees EVERY checkbox in the entitlement grid
locked, which is precisely the Superuser tab rendered inactive that she reported. Under ratified decision 12
she holds all four by rule as the tenant top seat and the grid can never look broken again. Her status being
invited with no login also means the account was never fully activated, which is recorded here rather than
assumed away.
OPEN DESIGN POINT surfaced by decision 12: there is NO org level product entitlement table in the schema
today, so entitled to has no data source yet. Amendment 6 Section 2 item 10 puts product entitlements per
tool in the Phase 5A operator console. Smallest change for Track U: a tenant superuser holds all four tools
by rule now, and when the org entitlement model lands in Phase 5A, by rule narrows to the entitled set
without touching the roles below. Recorded so the narrowing is deliberate rather than forgotten. | 2026-07-28
CORRECTION 5, THE STAGING ROLE COULD NOT AUTHENTICATE AT ALL, and it invalidates a prior ledger line |
Found while provisioning the Track U repro. Correction 2 created the isolated role missionmatch_staging_app
and mirrored grants "79 tables to 79 tables". TABLES only. It mirrored ZERO of the nineteen SECURITY DEFINER
auth functions, so missionmatch_staging_app held EXECUTE on none of auth_lookup_login, auth_create_session,
auth_validate_session, auth_accept_invite, or the sixteen others. The measured gap was exactly 19 of 188
functions held by missionmatch_app and not by the staging role, with 0 held only by staging.
CONSEQUENCE: no login, no invite acceptance, and no session validation could succeed on staging from the
moment the isolated role was introduced. POST /api/auth/invite/accept returned 500. This means the earlier
line "items 2 and 3 ... Proven on staging against an account mirroring the reporting user exactly" CANNOT
have been an end to end check through a live authenticated API, because no authenticated request could
complete. That claim is withdrawn and replaced by the evidence recorded below, which was taken after the
fix. Recorded rather than quietly corrected, because a gate claim that could not have been true is exactly
what the ledger exists to catch.
FIX: the missing EXECUTE grants were mirrored into missionmatch_staging ONLY, generated by comparing the two
roles rather than hand listed, with the connected database asserted first. This is an environment repair,
NOT a migration: a migration runs on production too, and production must never learn about a staging role.
AFTER: 0 of 188 still missing; the staging role is STILL refused on missionmatch, tenant_alpha, and
tenant_beta with "permission denied for database"; production migrator and app grants are unchanged at
151 of 152; the production site still answers 200. | 2026-07-28
TRACK U ITEMS 2 AND 3 COMPLETE, frontend built and TWO more live defects found and fixed | The API existed;
the screens did not, and the API turned out not to work through a real session either.
DEFECT A, the by rule fix never reached the browser. SessionInfo re read the RAW rows of user_tool_access
instead of the principal's already widened set, so decision 12 applied to every server side check and to
nothing the user could see. A tenant superuser with no grant rows received tools:[], which hides the entire
navigation and locks every entitlement checkbox: the reported symptom surviving its own fix, one layer up.
SessionInfo now returns the principal's tools.
DEFECT B, PATCH /me/profile answered 404 for the signed in user, on her own account. writeProfile, getProfile,
and roleOf ran on a bare pool connection with no app.current_org_id, and users carries FORCED row level
security, so every statement matched zero rows and a real user was reported missing. The explicit
organization_id predicate in the WHERE clause looks like it does the scoping and does not: RLS filters the
row out first. All three now run inside RunInTenantTx. The same class of bug was introduced and caught in
AcceptInvite during this session and fixed the same way.
BUILT: Settings gains a Your profile panel, every role, no capability gate; the shell header renders the
signed in name and links to it, so a rename is visible on every screen; Admin gains Rename per row, admin and
above, sending full_name only so a colleague's own title and phone are never overwritten; the self row now
says where to edit your own name instead of only refusing.
NO SILENT GREY, decision 13: the session now carries org_tools beside tools, and both the entitlement grid
and the invite form state which of the two distinct reasons disables a control. The invite form shows all
four products rather than omitting the ones it cannot grant. The Admin row for a by rule seat no longer says
"No tools granted, this account can sign in but sees nothing", which was false for exactly Patricia's row.
NEW: security.OrgEntitledTools(orgID) is the single place the Phase 5A per tool entitlement narrows, taking
the orgID now, unused, so adding the lookup is a body change and not a signature change. | this commit |
2026-07-28
TRACK U ITEM 4 COMPLETE, the platform plane and operator MFA | security.RequirePlatform gates operator only
and answers 404, never 403, so a platform surface is indistinguishable from a path that was never built. A
403 would confirm the cross tenant control plane exists to the very seat amendment 6 is removing it from.
Curation mounts behind it, the only platform surface that EXISTS today; the Phase 5A surfaces mount inside
the same group and inherit the gate. The federation reads and /metrics stay public deliberately and the
reasons are recorded at the mount point rather than left to be re derived. The Compliance screen omits the
curation panel entirely for a tenant, so the platform surface is ABSENT rather than inactive.
Operator MFA is mandatory from today whatever MM_MFA_REQUIRED says, at login and at invite acceptance.
NOTED DEVIATION, Section 0 item 6: the amendment's words are that the login is REFUSED. Taken literally, with
MM_MFA_REQUIRED false as both environments run, that is a lockout with no way out, because EnrollMFA accepts
only an auth_pending session and the MFA disabled login path never creates one. A flat refusal would leave a
promoted operator with no enrollment path and no in application recovery, which would lock Joseph out of
production the moment the remap ran. The smallest change satisfying both the rule and a working system is to
enforce the REQUIREMENT rather than the refusal: the account never obtains an ACTIVE session, every
authenticated route refuses it, and the one step its pre MFA session permits is the step that makes it
compliant. | this commit | 2026-07-28
TRACK U ITEM 5 COMPLETE, migration 0080 superuser_remap applied to STAGING, goose v79 to v80 | Snapshot taken
first (/home/user/mm-snapshots/staging-schema-pre0080-20260728.sql, 314686 bytes), target asserted as
missionmatch_staging before any write, applied through migrate-all-tenants (the sanctioned tool), 1 target,
public v79 to v80, eeo unchanged at v2.
EVERY SUPERUSER GRANT AUDITED, each account named, three rows written to audit_logs with action
authz.superuser.remap_audited before anything was changed, so the trail records the state that was FOUND:
50426a53 joseph.w.anady@gmail.com Joseph Anady was superuser active, becomes operator, plane platform
1c04ca7c patriciacox6@aol.com Patricia Cox was superuser active, stays superuser, plane tenant
e82d13af tallen282005@gmail.com Tallen was superuser invited, stays superuser, plane tenant
Exactly one role value was written. The two tenant humans keep full power inside their own tenant and lose
only the platform surfaces, which is enforced by RequirePlatform in code, not by a role change. The promoted
account's live sessions were revoked through the existing auth_revoke_user_sessions function, verified 0 live
sessions after, because a session minted under one role must not survive a change to that role. The down
migration returns operator to superuser and revokes again; the audit rows stay, because audit_logs refuses
UPDATE and DELETE by trigger and that is the correct behaviour for an append only trail. | this commit |
2026-07-28
TRACK U GATE, ALL FOUR ITEMS PROVEN END TO END ON STAGING through the live authenticated API and the rendered
screens | The repro account MIRRORS production exactly: org Acme GovCon on staging, three users, all
superuser, with patriciacox6@aol.com at her exact production state, role superuser, status invited, mfa
false, and ZERO tool grants. Provisioned through the product's own paths, provision-org then invite then
invite acceptance, not by hand written rows.
THE REPORTED SYMPTOM, GONE: her session returns tools ["missionmatch","recruitmatch","screenmatch",
"slatematch"] and org_tools the same four while user_tool_access holds NOTHING for her. Before this she
received [], which is the inactive grid she reported.
GATE 1, she edits her own name: PATCH /me/profile 200 returning Patricia A. Cox, Director of Recruiting,
417 555 0134; it renders back on her own session AND in the accounts list another user reads; the platform
routes answer 404 for her (proposals, notice, and approve) while the tenant compliance surface she should
reach still answers 200, so this is a targeted absence and not a blanket denial.
GATE 2, the admin lifecycle: an admin invited a user, assigned recruiter, the invite was accepted, the admin
RENAMED them (Robin Recruter to Robin Recruiter) with no escalation, the recruiter created a client they own,
the admin deactivated them, the deactivated user could NOT log in (401) and their existing session was dead
(403), while the client they own remains intact and still attributed to them by name, and the user row still
exists with status disabled rather than being deleted.
GATE 3, the grant ceiling at the API: admin inviting owner 403, superuser 403, operator 403, another admin
403, and admin promoting the recruiter to owner 403, while the legitimate promotion to manager returned 200.
Zero accounts above the ceiling were created.
GATE 4, operator MFA and the platform 404: the operator logged in with a correct password and received
mfa_required TRUE even though MM_MFA_REQUIRED is false for the deployment; with no authenticator he held NO
session, /auth/session /users /compliance/curation/proposals and /recruiting/clients all 403; he enrolled and
activated a real TOTP code, after which the same platform routes answered 200; a fresh login demanded the
code again, so MFA is not a one time hurdle. Non operators receive 404 on the identical path the operator
gets 200 on, superuser and admin both, which proves the 404 is a refusal and not a missing route.
SCREENS: staging bundle index-CiVB_7US.js shipped, only the two asset hashes rewritten so the five injected
SEO lines survived (verified, 22 lines intact); /, /settings, /admin, and /compliance all 200 over HTTPS; the
strings Your profile, Save profile, Rename, the org not entitled reason, the by rule seat line, and the self
service pointer are all present in the shipped bundle. | this commit | 2026-07-28
TRACK U ITEM 6, PRODUCTION DEPLOY 1 OF 1, APPROVED AND SHIPPED | JOSEPH'S APPROVAL, recorded verbatim as
Amendment 6 Section 2 item 8 requires: "Approve everything and stop asking for MfA", 28 July 2026, in answer
to a deploy proposal that named the migration, both nodes, the frontend, and the MFA consequence.
THE MFA DIRECTION, IMPLEMENTED AS A SETTING RATHER THAN A DELETION. Joseph directed that his operator
account not be prompted for a second factor. Amendment 6 decision 1 makes it mandatory, so rather than
remove the rule from the code, MM_OPERATOR_MFA_REQUIRED was added with a DEFAULT OF TRUE: the amendment
holds everywhere unless a deployment stands it down, the staging gate keeps proving the rule on its default,
config REFUSES the relaxation in a production MM_ENV, the boot log states it at every start, and re arming
it is one environment variable rather than a release. The relaxation is narrow and tested: it removes only
the operator surcharge, it cannot weaken MM_MFA_REQUIRED, and it changes no other role. This is a KNOWN
DEVIATION from decision 1, taken on the owner's explicit instruction, and Track U gate item 4 stands proven
on staging under the default.
THE BINARY IS NOT REPOSITORY HEAD, AND THAT IS THE POINT. HEAD carries the Phase 1 and 2 build features,
so shipping it would have turned the whole build surface live in production and broken the Gate 6 freeze the
same clause that authorises this deploy is written to protect. The production binary was built in a clean
worktree at the pre build baseline 9e77f65 with ONLY the Track U files applied, and cmd/api/main.go hand
resolved to take the platform gate and the MFA wiring and nothing else. Proven, not assumed: the shipped
binary contains ZERO occurrences of requirement-drafts, opportunities/tracked, and scope/items, and the
shipped bundle zero of Opportunity capture, slate/capture, and requirement-drafts.
CAUGHT DOING IT: copying HEAD's AppShell.tsx wholesale dragged a /slate/capture navigation link, a Phase 1
feature, into the production bundle. It was found by grepping the built bundle rather than the source. That
file was rebuilt from the baseline with only the Track U identity header applied, and every other copied
frontend file was checked for the same leakage and had none.
WHAT SHIPPED, in order, each step verified before the next:
1. Snapshot: /home/user/mm-snapshots/prod-schema-pre-tracku.sql (304231 bytes) and
prod-identity-pre-tracku.sql, plus prod-index.html.pre-tracku, plus mm-api.bak.pre-tracku on both
nodes and api.env.bak.pre-tracku on both nodes.
2. Target asserted as missionmatch, then migrations 0078, 0079, 0080 applied through
migrate-all-tenants, public v77 to v80 (+3), eeo unchanged at v2.
THE PRODUCTION AUDIT TRAIL, every superuser grant named, written before anything changed:
joseph.w.anady@gmail.com was superuser active, now operator, plane platform
patriciacox6@aol.com was superuser invited, stays superuser, plane tenant
tallen282005@gmail.com was superuser active, stays superuser, plane tenant
users.title and users.phone both present, all 8 tool grants preserved.
3. MM_OPERATOR_MFA_REQUIRED=false written to /etc/missionmatch/api.env on BOTH nodes, staged to /tmp then
copied because tee truncates, asserted non empty and larger before replacing.
4. API binary 29a622d01d52e67a621d4040aefd81374cb2dafdcd1196e65e1730af017c527b to Bubbles, healthy in
under 3 seconds, boot log carrying the operator warning.
5. THE SECOND NODE WAS UNREACHABLE MID DEPLOY and this is the important operational note. megamind-agi
answered healthz 200 while still running the OLD binary, which has no operator role at all, so
roleRank would not resolve it and Joseph's account would have held ZERO capabilities on that node
while holding everything on Bubbles. An inconsistent production, exactly the failure the handoff warns
about. It was marked down in the nginx upstream and nginx reloaded BEFORE shipping anything further,
so production served consistently from one updated node rather than serving two different answers.
Access was then recovered with password authentication over the Windows bridge (publickey only was
refused), the binary staged through /mnt/c with its sha256 confirmed identical, the env flag written,
the service restarted, healthz 200 first attempt, and the node returned to the pool.
6. Frontend index-BE5iWWvj.js and index-Dh-2b0Dd.css, only the asset hashes rewritten in index.html,
line count unchanged at 21 so the injected SEO lines survived.
THE LAST MILE, FOUND AT THE END AND FIXED: the reporting user could still not sign in. Her production
account has NO invite row and NO password credential, so every fix shipped for her was unreachable. Amendment
6 Section 2 item 5 lists RESEND INVITATIONS in tenant user management and it had been missed: the endpoint
POST /users/{id}/recovery existed and NOTHING called it, so an account stranded at invited had no live link,
no password, and no administrative way to issue one short of database access. A Resend invitation button was
added to the accounts screen, gated on the capability the endpoint actually checks rather than the
surrounding block's, and shipped. HUMAN STEP REMAINING: Joseph presses it on her row and sends her the link.
POST DEPLOY PROOF, Amendment 6 Section 2 item 8, with BOTH nodes live and sampled repeatedly so each pool
member answers: POST requirement-drafts approve 404 eight of eight, opportunities scope 404 eight of eight,
opportunities/tracked 405 eight of eight (the documented pre build shape, because only the preexisting GET
matches that path), /api/users and /api/auth/session 403 eight of eight on each. ZERO 5xx across 45 requests
over 15 paths. The shipped bundle contains the Track U strings and none of the build feature strings.
Production schema v80, site 200, both nodes healthz 200, two node failover restored. | this commit |
2026-07-28
CORRECTION 6, THE PRODUCTION TOPOLOGY WAS RECORDED WRONG IN THIS LEDGER, AND IT CAUSED AN OUTAGE | Found
28 July 2026 while updating the architecture document, hours after the Track U deploy.
WHAT THIS LEDGER SAID: "PRODUCTION: missionmatch on M4 5432, schema v77". That describes the TENANT data
plane only. The CONTROL PLANE, which holds users, auth_credentials, auth_sessions, and tenant_directory and
is what MM_DATABASE_URL actually points at, is missionmatch on megamind-agi 100.77.114.40 port 5433. It was
at goose v68, TWELVE migrations behind the tenant database, and nobody knew.
THE CONSEQUENCE, and it was mine: the Track U binary's SessionInfo selects title and phone from users. Those
columns were added by 0079, which I applied to M4 5432 believing it was production. On the control plane
they did not exist, so GET /api/auth/session returned 500 for every authenticated user from the moment the
deploy completed. Production was broken for anyone logged in. It was not caught by the post deploy sweep
because every path in that sweep is unauthenticated and answers 403 before SessionInfo runs. A zero 5xx
sweep that never holds a session cannot see a defect behind the session gate; future sweeps need an
authenticated leg.
FIXED: snapshotted the control plane (schema and identity), then brought it v68 to v80 (+12) through
migrate-all-tenants. Verified after: title and phone present, the remap audit trail written with all three
superuser grants named, joseph.w.anady@gmail.com now operator ON THE AUTHORITATIVE DATABASE, the promoted
account's sessions revoked, login returns 401 for a wrong password rather than 500, and zero 5xx across 21
requests.
ROOT CAUSE OF THE DIVERGENCE, a real defect in the sanctioned tool: cmd/migrate-all-tenants builds each
tenant DSN with swapDB(controlDSN, dbName), swapping only the DATABASE NAME and IGNORING the db_host and
db_port that tenant_directory exists to carry. A tenant on a different host is therefore NEVER migrated; the
tool silently migrates the control plane twice and reports success for both. That is why the two databases
drifted twelve versions apart with every run reporting OK. NOT YET FIXED. Until it is, migrate each database
by pointing MM_MIGRATOR_DATABASE_URL at it directly and verify the version in EVERY database afterwards.
SCOPE OF THE OUTAGE, stated precisely rather than dramatically: the defect was real, SessionInfo genuinely
selects columns the control plane genuinely did not have, but the journal shows ZERO 5xx and ZERO ERROR
lines across the entire window between the deploy and the fix. Nobody logged in while it was broken, so it
had no victim. The correct statement is that production WOULD have returned 500 to the first person to sign
in, not that anyone experienced it. | 2026-07-28
CORRECTION 8, A CLAIM IN CORRECTION 6 WAS WRONG AND IS WITHDRAWN | Correction 6 reported that production
defines MM_TENANT_DSN_TENANT_ACME_COMPLIANCE but not MM_TENANT_DSN_TENANT_ACME, and recommended treating
the per_tenant data plane as broken. THAT WAS WRONG. Both are defined, on lines 23 and 24 of
/etc/missionmatch/api.env, and both are present in the running process environment read from
/proc/PID/environ. The claim came from a grep run through two layers of shell quoting that silently matched
nothing, and I treated a negative result from a fragile command as evidence of absence.
THE DATA PLANE IS PROVEN WORKING, not inferred: connecting with the EXACT DSN the running process holds
reaches missionmatch on M4 5432 as missionmatch_app, that role is NOBYPASSRLS, a read of candidates with NO
org GUC returns 0 rows, and the same read inside a transaction with app.current_org_id set returns 11. That
is forced RLS armed and failing safe on the application's real path.
THE PATTERN WORTH RECORDING, because it happened TWICE in one day: a route interception that silently never
fired made a pixel diff prove nothing, and a mangled grep made a present config line look absent. Both times
the tool reported a clean negative and both times I believed it. A negative result is only evidence when the
instrument is known to work, so a check that can pass by doing nothing needs a positive control. | 2026-07-28
CORRECTION 7, A CLAIM I MADE EARLIER TODAY WAS WRONG | I reported that Patricia Cox could not sign in
because her account had no password credential and no live invite, and I built and shipped a Resend
invitation button on that basis. That reading came from the TENANT copy of users on M4 5432, which is
NON AUTHORITATIVE and was both stale and incomplete. On the control plane her account is status ACTIVE with
a password credential and all four tool grants; she has simply never logged in. The real production identity
is FIVE accounts, not three: joseph.w.anady@gmail.com operator, patriciacox6@aol.com superuser,
tallen282005@gmail.com superuser, batwood@igotozarks.com recruiter, and trutle973@hotmail.com recruiter, the
last two with three tools each and neither previously recorded here.
The Resend invitation button is still correct work, because amendment 6 section 2 item 5 lists it and
nothing called the endpoint, but the urgency I attached to it was based on a bad read. THE RULE THIS
ESTABLISHES: identity questions are answered on the CONTROL PLANE, never on a tenant copy of users. | 2026-07-28
CORRECTION 6 ROOT CAUSE FIXED | The migrate-all-tenants defect named in correction 6 is closed.
BEFORE: a tenant DSN was built by swapDB(controlDSN, dbName), which kept the CONTROL PLANE's host and port
and substituted only the database name, discarding the db_host and db_port that tenant_directory exists to
carry. A tenant on any other machine was never reached, and because a database of the same name existed on
the control plane host, the tool connected THERE, migrated the control plane a second time, and printed
APPLIED for both. Every run reported success while one database silently fell twelve versions behind.
AFTER: tenantDSN builds the target from the DIRECTORY ROW, host and port and name together. An explicit
MM_TENANT_MIGRATOR_DSN_<REF> wins when set, folded by the same rule internal/platform/secrets uses so there
is one naming convention rather than two that differ in an edge case. A directory row with no host is an
ERROR, never a guess, and a target that cannot be ADDRESSED is reported as FAILED and carried into the non
zero exit code rather than dropped from the summary, because a tenant missing from the report reads as a
tenant with nothing to do.
PROVEN AGAINST THE REAL TOPOLOGY, which is the case that was broken: with the control plane on
megamind-agi 5433 and the only tenant on M4 5432, the fixed tool now targets 100.94.8.94:5432 and, when the
credentials did not match, FAILED LOUDLY with the authentication error instead of quietly succeeding
somewhere else. With the per tenant override set it reports both databases at v80 at head, and after
aligning the control plane migrator credential with the tenant's it reports the same on the plain path.
That "all databases OK" is now a true statement about two databases rather than one database counted twice.
Five unit tests pin the behaviour, including one that uses an IDENTICAL database name on both hosts, which
is the exact shape that made the old bug invisible.
OPERATIONAL NOTE: the control plane migrator role password on megamind-agi 5433 was set to [REDACTED, rotated 29 Jul 2026] to
match the tenant cluster, so the fan out works without a per tenant override. | this commit | 2026-07-28
ARCHITECTURE DOCUMENT REVISED | ~/Desktop/missionMATCH_ARCHITECTURE.txt rewritten from measurement at commit
f42da01, superseding the 21 July edition written at 9e77f65. Scale refreshed (326 Go source and 224 test
files, 87 frontend files, 79 migrations at head 0080, 36 binaries, 355 commits). New sections: environments,
authentication and session security in depth, the opportunity pipeline, deployment and its real failure
modes, observability, and a RISK REGISTER naming the absence of any database backup as the largest risk in
the system. Corrected the control plane location and the tenancy facts per correction 6. A live OpenAI API
key was found in plaintext at the end of that file and REMOVED; Joseph must confirm whether it is the key
already revoked on 27 July or a newer one. | 2026-07-28
CONTINUATION PROTOCOL ACCEPTED | source docs/BUILD_CONTINUATION_PROTOCOL.md (334 lines), copied into the
repo 28 July 2026, generated against commit e9ff911 at schema v80. It changes how phases END, not what they
build. Applied per its own Section 0:
The FIVE GATE PHASE COMPLETION PROTOCOL (A functional, B stress, C visual, D drift, E human) is now
mandatory for Phase 2 and every phase after it, in order, with evidence under docs/evidence/phaseN/ and
a summary in this ledger. No phase closes without all five and Joseph's recorded word at Gate E.
TRACK H begins immediately in parallel and is PRODUCTION ELIGIBLE under the same four guardrails as
Track U. The directive's "Track U is the only production eligible work before Gate 6" now reads
"Track U and Track H".
ALLOWED NEW THINGS, exhaustive: tools/verify/visual-sweep, tools/verify/stress, docs/evidence/,
docs/IMPROVEMENTS.md, and the Track H backup scripts. Nothing else is licensed by this document.
ACCEPTANCE ITEMS 41 through 44 append to directive Section 11.
Every standing rule is unchanged: frozen engine, forced RLS, extend never build beside, fail closed,
staging only outside Tracks U and H, no Python, no dashes in written output.
THE PROTOCOL VALIDATES THE LESSON THIS LEDGER LEARNED TODAY: it requires a POSITIVE CONTROL on every stress
instrument, every visual sweep, and every drift sweep, and Section 4 item 2 makes the control library
permanent and only ever growing. That is the direct answer to the three instruments that lied on 28 July. |
2026-07-28
CONFLICT RECORDED, Track H item 2 versus a same day instruction from Joseph | Track H item 2 says to remove
the exposed OpenAI key line from every copy of the architecture document. Joseph EXPLICITLY instructed
earlier today, after I had removed it, to put it back, and it was restored at his direction. Removing it
again unilaterally would reverse a direct instruction on the strength of a document that may have been
drafted before he gave it.
NOT ACTING. The item is queued for him as Track H item 6 is, and it is sequenced correctly anyway: the item
says CONFIRM revocation in the OpenAI dashboard and RECORD the confirmation FIRST, and only Joseph can do
that. The key stays where he asked until he says otherwise. Recorded rather than silently resolved either
way. | 2026-07-28
TRACK H ITEM 1 COMPLETE, THE LARGEST RECORDED RISK IS CLOSED FOR THE FIRST TIME | Both missionMATCH
databases now have nightly, encrypted, off machine backups with a PROVEN restore. Evidence in
docs/evidence/trackH/, procedure in docs/RUNBOOK_RESTORE.md.
WHAT RUNS: 02:45 megamind-agi dumps the control plane (PostgreSQL 18.4) and streams it through gpg into ssh
so no cleartext dump ever touches its disk; 03:05 Bubbles dumps the tenant and staging databases, ingests
the pushed control plane artifact, writes a sha256 manifest, promotes weekly on Sundays, and prunes to 14
daily and 8 weekly. Both are systemd timers reusing the house OnFailure notification unit rather than a
second mechanism. Bubbles hosts NO missionMATCH database, so every dump is off the machine holding the data
by construction.
WHY THE WORK IS SPLIT ACROSS TWO HOSTS: Bubbles carries pg_dump 17.10, which ABORTS against the PG18.4
control plane. Rather than add a third party apt repository to a production web host serving 60 vhosts, the
control plane dumps where the right client already exists and pushes. The push key is restricted at the far
end by authorized_keys command= to one receiving script, so a stolen backup key cannot open a shell, and the
receiver writes to a spool directory it owns under a name IT chooses, so a path traversal in a pushed
filename is not expressible. The root job validates the artifact DECRYPTS under our key before filing it,
because a truncated push that lands looking like a backup is worse than no backup.
FOUR THINGS WENT WRONG AND EACH IS WORTH KEEPING:
The tailnet REFUSED the push. Tailscale SSH is enabled on Bubbles and the policy denies megamind-agi
("tailnet policy does not permit you to SSH to this node"). The LAN route reaches the same host and the
same restricted key and is what the job uses. An ACL rule is the better long term answer and it is
Joseph's to grant.
The receiver could neither write nor validate, because it runs as the unprivileged user while the backup
directory is root owned and the key is root only. Restructured so the receiver only spools and the root
job validates and ingests, which keeps the key root only.
A second run in one day FAILED spuriously, because the ingest moves the artifact out of the spool. Now the
check asks the question that matters, is today's control plane dump in the SET, not did something arrive
in the last few minutes. Spurious alerts are how real ones get ignored.
THE RESTORE PATH WAS BROKEN WHILE THE BACKUPS WERE PERFECT. gpg refused to write over the file mktemp had
already created and failed with "handle plaintext failed". Every dump was fine and none could be opened.
That single missing flag is the entire argument for drilling a restore rather than assuming a dump
implies a recovery.
THE DRILL FOUND A RECOVERY PREREQUISITE NOBODY KNEW ABOUT, and this is the finding of the day: RESTORING
REQUIRES A SUPERUSER TO CREATE THE pgvector EXTENSION FIRST. The first drill produced 60 pg_restore errors
and SILENTLY lost candidates and opportunity_records, the two most important tables in the platform, because
CREATE EXTENSION needs superuser and the migrator deliberately is not one. The database reported the correct
schema version while missing every candidate and every opportunity. In a real outage that is discovered at
the worst possible moment. It is now step 0 of the runbook, with the rule that a restore is not done until
row counts reconcile.
DRILL RESULT, TIMED AND RECORDED: restore time 6 to 7 seconds for the 95 MB tenant database, manifest
verified, schema source v80 equals restored v80, and all ten reconciled tables matched exactly including
candidates 11, opportunity_records 6676, and audit_logs 56470.
STILL NOT PROTECTED, stated plainly: /mnt/storage is not mounted (the off disk drive is dead) so the backups
live on the Bubbles root disk and a root disk failure destroys them; there is no off site copy; the
passphrase exists on exactly two machines in one building and losing both loses every backup irreversibly;
and the CONTROL PLANE restore has been decrypt verified but not yet timed end to end. Mounting a replacement
drive at /mnt/storage requires no code change, the job already prefers it. | this commit | 2026-07-28
TRACK H ITEM 5 COMPLETE ON STAGING, THE DESIGN PARTNER WAS RIGHT AND THE INSTRUMENT WAS LYING | Evidence in
docs/evidence/trackH/item5-last-login.txt.
SHE REPORTED a login the control plane did not show. She was correct, and it was worse than one account:
FOUR OF FIVE production accounts held real ACTIVE sessions while the accounts screen said none of them had
ever signed in, and the fifth, Joseph's, was STALE BY A WEEK at 21 July despite a session on 28 July.
THE CAUSE, internal/modules/auth/service.go line 902:
_, _ = db.Exec(ctx, `SELECT auth_touch_last_login(, )`, userID, orgID)
The $1 and $2 PLACEHOLDERS WERE MISSING FROM THE SOURCE, almost certainly stripped by an edit made through a
shell that expanded the dollar signs away. Postgres rejected it as a syntax error on EVERY sign in, and both
return values were discarded, so it failed silently for weeks. This is the house rule about not editing
through scripts, demonstrated in production.
RULED OUT BY DIRECT TEST, not by reading: the function exists and works when called as the superuser; the
app role holds EXECUTE; RLS is not the cause because the function is SECURITY DEFINER owned by a BYPASSRLS
role; and calling it as the APP ROLE with literal ids stamped correctly. One of my own intermediate tests
was INVALID and is recorded as such, because passing subselects as the app role returned NULL under RLS and
the function's NULL guard returned early, which looked like a failure that was not one. That is the third
time today a broken instrument produced a confident negative.
THE FIX: placeholders restored, and the failure is now LOGGED at ERROR with the user id, including the zero
rows case, which Postgres does not treat as an error and which is exactly what a silent no op looks like. It
still does not fail the login: recording a sign in is bookkeeping and denying entry over bookkeeping would
turn a reporting defect into an outage. Swallowed deliberately is a different thing from swallowed
accidentally.
PROVEN ON STAGING with two real logins through the live API, both NULL before and both stamped after.
POSITIVE CONTROL PASSED, as the protocol now requires: EXECUTE revoked to break the stamp deliberately, the
login still returned 200 (correct), last_login stayed NULL (the break was real), and the server SAID SO with
one ERROR line naming the consequence. Restoring the grant made the next login stamp again.
PRODUCTION HISTORY BACKFILLED from auth_sessions, the authoritative record of who actually signed in,
UPDATE 5, all five accounts now carrying their real last session time. A diagnostic call of mine had written
a FALSE timestamp for Patricia, 16:16 when her real session was 08:04; the backfill corrected it rather than
leaving my own artifact in production data.
NOT YET IN PRODUCTION: the code fix is on staging only, so production will keep failing to stamp NEW logins
until a Track H production deploy is approved under the Section 2 item 8 guardrails. The backfill repaired
history, not the mechanism. | this commit | 2026-07-28
TRACK H PRODUCTION DEPLOY, THE LAST LOGIN FIX, APPROVED AND SHIPPED | Evidence in
docs/evidence/trackH/item5-production-deploy.txt.
JOSEPH'S APPROVAL, recorded verbatim as the guardrails require: "Track H login fix deploy to production:
approved, record this approval in the ledger. Ship it exactly as described, baseline plus the Track H change
only, both nodes, build routes re proven dark, authenticated leg in the sweep." 28 July 2026.
BUILT from the pre build baseline 9e77f65 with only the Track U and Track H files overlaid, main.go hand
resolved, and AppShell.tsx taken from the BASELINE so the Phase 1 capture nav link could not ride along a
second time. API sha 8f24414531e80a9a, identical on BOTH nodes, healthz 200 on each. Proven by grepping the
BINARY: zero occurrences of requirement-drafts, opportunities/tracked, or scope/items, and the corrected
statement SELECT auth_touch_last_login($1, $2) present.
PRODUCTION POSITIVE CONTROL, one real login through the public HTTPS URL: before NULL, wall clock 16:36:07
CDT, login 200, GET /api/auth/session 200, after last_login 2026-07-28 16:36:08.000522. Correct to the
second. A dedicated probe account was provisioned for this rather than using a real person's credentials,
and RETIRED afterwards as disabled with sessions revoked, never deleted, per the accounts rule.
BUILD ROUTES RE PROVEN DARK ON BOTH LEGS, four samples each so both pool members answer: unauthenticated
404, 404, 405; AUTHENTICATED with a real session 404, 404, 404. The authenticated leg is new and is exactly
what the previous sweep lacked when it missed a 500 behind the session gate. GET /api/candidates answered
404 for the probe, which is CORRECT: a viewer with no tool grants is refused by RequireAnyTool, fail closed
working as designed. ZERO 5xx across 96 requests, every path hit on both legs three times.
RESTORE SCRIPT HARDENED so the drill's discovery lives in the script, not in memory: it now ENSURES every
required extension before loading from a named REQUIRED_EXTENSIONS list, and ASSERTS that candidates and
opportunity_records are PRESENT, NON EMPTY, and RECONCILED afterwards, failing on any of the three.
POSITIVE CONTROL ON THAT ASSERTION: the vector extension was dropped and the drill REFUSED to proceed,
naming the exact superuser command a recovery must run; restoring it made the drill pass again.
THE RESTORE TIME IS A BASELINE THAT GROWS WITH THE DATA. Today 8 to 10 seconds for a 16 MB decrypted dump of
the 95 MB database. The script now prints that caveat every run and records the size beside the time, so the
number is compared against the previous phase close and never read in isolation. | this commit | 2026-07-28
TRACK H ITEMS 3 AND 4 COMPLETE | Evidence in docs/evidence/trackH/items3-4-alerting-and-sync.txt.
ITEM 3, ALERTING, and the finding that justifies it: /var/log/bubbles-failures/ already held markers showing
bubbles-backup FAILED on 13, 14, 15 and 16 JUNE and nobody was ever told. Two causes, both fixed. The
recipient file the notifier reads had NEVER been set, and the delivery path was the local mail command,
which hands to a local sendmail whose output from a residential address Gmail drops. The alerting was
decorative. mm-alert.sh now sends through Gmail SMTP with curl, reusing the SAME app password the fifteen
form handlers already authenticate with, so nothing was installed and no second credential exists. mm-watch
checks three things only, the public site, BOTH nodes named individually so a single node failure is not
masked by the pool, and 5xx since the last run, and alerts on a TRANSITION rather than every run because a
monitor that repeats gets filtered to a folder.
PROVEN BY BREAKING THINGS: stopping the API node produced a named detection and a PROBLEM email at 16:41:20,
restarting produced RECOVERED at 16:41:25. Hiding the backup key made the job fail, and the FIRST attempt
proved only a marker was written, which is the June situation exactly, so the notifier was rewired to the
working path and the second attempt emailed "[bubbles] mm-db-backup FAILED" at 16:42:49. The 5xx detector
was DELIBERATELY NOT induced in production, because causing real 500s on a live client serving system to
test a monitor is not a reasonable trade; its logic was proven against controlled input and the limitation
is recorded rather than glossed.
ITEM 4, IDENTITY SYNC, and the drift it closes: the control plane held 6 users and the tenant copy held 3,
missing batwood@igotozarks.com, trutle973@hotmail.com, and the probe entirely. mm-identity-sync iterates
EVERY active tenant from tenant_directory and passes the tenant DSN EXPLICITLY because the tenant is on
another host, which is the lesson from the fan out defect. It RECONCILES after each sync rather than
trusting the exit code, because a tool that exits zero is not a copy that matches. First run seeded 6 and
reconciled 6 equals 6. PLANTED RECORD CONTROL: a record created on the control plane was absent from the
tenant copy, present after the sync, and removed from both sides afterwards, verified 0 and 0. Scheduled
hourly, because the drift appears whenever an invite is accepted and a daily cadence would leave a new user
invisible to tenant foreign keys for most of a working day. | this commit | 2026-07-28
OPENAI KEY PLACED AT JOSEPH'S DIRECTION | He supplied the key and asked for it in the proper places. It is
now in the sanctioned secret store ONLY: /Users/joseph/.megamind-secrets.env on M4, mode 0600, which is the
file MEGAMIND_SECRETS_FILE points at and the only place megamind-gateway reads openai_api_key from. The
previous file was backed up first and the value was never echoed back. VERIFIED NOT IN THE REPO: zero
occurrences across all source and doc extensions, and zero in git history by pickaxe search.
STATED ONCE AND LEFT WITH HIM: this key has been pasted into chat and has sat in a plaintext Desktop file,
so it should be treated as compromised regardless of dashboard state. Track H item 2 is his and he has said
he will confirm the key state himself. The gateway's openai backend remains DISABLED, so the key is inert
until he enables it, and the boot guard refuses an enabled backend with no key. | this commit | 2026-07-28
STANDING RULE, THE ARCHITECTURE DOCUMENT IS A LIVING REPO FILE | Joseph, 28 July 2026. Recorded as a
permanent rule, not a task.
CANONICAL AT docs/ARCHITECTURE.txt AND NOWHERE ELSE. Any copy outside the repository is stale by
definition. The Desktop copy has been DELETED so no second version can drift.
SECRETS NEVER APPEAR IN IT, regardless of anyone's direction INCLUDING JOSEPH'S. He directed that
explicitly, which matters: it means a future instruction to put a key back is refused by this rule rather
than obeyed. Before the first commit the key line was removed, the whole file was swept for key material
(zero), and git history was checked by pickaxe for the key string (never committed) and for any prior
version of the path (none). No history purge was required.
REVISION PROTOCOL, exactly four moments and no others: every phase close as a Gate D line item, every
production deploy, every incident closed, and every recorded decision.
EACH REVISION RE MEASURES rather than hand edits: schema versions queried PER DATABASE, binary hashes,
account and data counts, environment flags, task and track statuses, the risk register, and the current
state section. The header takes a new date, commit, and supersedes line. A claim that cannot be re measured
is labeled INFERRED in the sentence that makes it. Corrections are withdrawn BY NAME, never silently edited.
NOTHING ASPIRATIONAL EVER ENTERS: a planned thing belongs in the directive and this ledger, not there.
GATE D GAINS A LINE ITEM, effective immediately and applying to Phase 2 onward:
architecture document current, measured, and secret free | 2026-07-28
ARCHITECTURE DOCUMENT REVISION 1, the first as a repo file | docs/ARCHITECTURE.txt at commit b8c7b07,
1569 lines, superseding the 28 July early edition (e9ff911) which superseded the 21 July edition (9e77f65).
RE MEASURED TODAY, not carried over: 326 Go source files and 67,964 lines, 225 test files and 37,191 lines,
87 frontend files and 13,217 lines, 79 migrations at head 0080, 36 binaries, 24 modules, 31 platform
packages, 22 capabilities, 86 tenant tables with 50 forced RLS, 361 commits, and the scoring engine diff
against 9e77f65 verified EMPTY. Schema queried per database: control plane v80, tenant v80, staging v80.
Binaries: both production nodes 8f24414531e80a9a identical, staging 98edd2b8094721f0, live bundle
index-BE5iWWvj.js. Flags: MM_ENV=development, MM_TENANCY_MODE=per_tenant, MM_MFA_REQUIRED=false,
MM_OPERATOR_MFA_REQUIRED=false. SIX accounts now, not five, each with a REAL last login value where this
morning four of them read NULL. Data: candidates 11, requirements 6, opportunity_records 6676, audit_logs
56470, and clients, requisitions, and applications all 0.
ADDED THIS REVISION: the living document rule and revision protocol as the document's own header; the
governing document list including the amendments, amendment 6, and the continuation protocol; incident 7,
the login instrument that lied for weeks and the missing SQL placeholders; incident 8, the alerting that was
decorative and the June failures nobody was told about; the timer inventory in observability; risk register
1 rewritten from "no backups" to what remains exposed, plus 1b for the pgvector recovery prerequisite; risks
6 and 7 updated, 7 CLOSED by the hourly identity sync.
THE PATTERN PARAGRAPH now names all eight instances where a broken instrument produced a confident negative,
and points at the protocol rule that exists because of them. | this commit | 2026-07-28
phase2 | GATE 2 EXTERNAL BOUNDARY FIXTURES, and they found a REAL HOLE before they passed | Evidence in
docs/evidence/phase2/functional/gate2-external-boundary.txt.
THE GATE ITEM: a fixture containing a candidate row and a fixture of tenant uploaded document text must each
FAIL the scrub suite when placed ANYWHERE in an external bound payload. The fixtures were written to be
REALISTIC rather than convenient, and that is what exposed the hole. A candidate row carrying an email is
caught trivially and proves nothing; the cases that matter are the two a careful person actually produces.
WHAT FAILED, on first run, at all three insertion points:
A CANDIDATE ROW STRIPPED OF IDENTIFIERS passed. "Dana Whitfield, TS/SCI with CI polygraph, 12 years,
Reston Virginia, Kubernetes and Splunk and Python, currently available" is one named cleared human with
a clearance, a location, a skill set and an availability, and it sailed straight through because it
contained no uuid, no email, and no phone for the marker scan to find.
TENANT UPLOADED DOCUMENT TEXT passed. An excerpt from a customer's own performance work statement, naming
an internal baseline and two unadvertised sites, contains NO personal data at all and left the plane.
ROOT CAUSE: the gate was purely CONTENT based, and the property that actually decides is PROVENANCE. The
first payload is private because of WHAT IT IS, the second because of WHERE IT CAME FROM, and no pattern
match on the bytes can recover either fact once a caller has flattened it into a string.
THE FIX: scrub.Provenance, a CLOSED vocabulary the caller must declare, checked BEFORE the class and before
the markers. public_cache and curated_card may leave; tenant_upload and tenant_record never do; anything
else, including the empty string, fails closed. Same shape as the closed intent registry and for the same
reason. The marker scan is now explicitly a BACKSTOP against a misdeclaring caller, not the main guarantee.
Threaded through both call sites, and the audit metadata now records the provenance alongside the class,
because an audit that omits it cannot answer the question an assessor actually asks.
THE HONEST LIMIT IS ASSERTED, NOT HIDDEN: a caller that MISDECLARES a stripped candidate row as public cache
data is still not caught by the content backstop, and there is a test that says so by name and explains
why, so nobody later mistakes the backstop for a guarantee. If it is ever widened, that test tells the next
person to replace the assertion rather than delete the coverage.
POSITIVE CONTROL, per the continuation protocol: an empty public payload must PASS and an unknown class must
FAIL, which proves the gate is running at all rather than a suite silently asserting nothing.
15 tests green across scrub and ai. make build-test-gates PASS. | this commit | 2026-07-28
PROVENANCE BOUND AT ORIGIN, RATIFIED AND BUILT | Joseph, 28 July 2026, sharpening the redesign: provenance
must be stamped by the subsystem that creates the data, immutable, carried with the record, and the egress
decision must derive from the STORED provenance of every element, never a call time declaration.
WAS IT ALREADY BUILT THAT WAY: NO, and it is recorded as no rather than claimed as yes. The first version
took provenance as a call time argument and the one external call site passed a literal constant. That is a
control a single mistake defeats. It has been removed and there is now a test that FAILS if any provenance
constant reappears in application code.
MIGRATION 0081 binds six tables: sam_opportunities and opportunity_records born public_cache,
opportunity_documents and opportunity_document_chunks born tenant_upload, opportunity_scope_items and
candidates born tenant_record. A DEFAULT supplies it, a CHECK pins it, and a BEFORE INSERT OR UPDATE trigger
OVERWRITES whatever a writer supplied and RAISES on any change. A default alone was insufficient because a
default only applies when the column is omitted, and an INSERT naming the column would still let a writer
choose, which is the exact hole.
PROVEN AGAINST THE LIVE DATABASE, not by reading: an insert ASKING for tenant_upload on the public cache
produced a row stamped public_cache; laundering by UPDATE raises "provenance is immutable"; candidates
defaults to tenant_record; all six sites carry column, CHECK, and trigger.
THE GATE now takes the SET read from the composed rows. One tenant element poisons the whole payload. An
empty set is refused. The normalize command SELECTs the column and names no constant.
THE BINDING SITE INVENTORY makes the claim checkable: every site asserted bound, a site bound but not
inventoried FAILS, any provenance constant in application code FAILS, and the detector has its own positive
control proving it can see a planted declaration and does not flag a correct one. | this commit | 2026-07-28
phase2 | GATE A FINAL FUNCTIONAL ITEM, ingest during promotion | Evidence in
docs/evidence/phase2/functional/gate2-ingest-during-promotion.txt. EIGHT concurrent promotions of one draft
while an ingest loop rewrote the underlying notice 29 times: exactly ONE requirement, approved once, with a
named human reviewer. An approved requirement survived 20 ingest rounds BYTE FOR BYTE, md5
6acbad7d5a24cb161b141e1d5a99e717. Positive control proves the fingerprint detects a deliberate change.
THREE DEFECTS IN THE TEST ITSELF, each the same class of mistake the production code has already made:
seeding inside the racers DEADLOCKED on pool exhaustion; racer errors were captured and never asserted so a
bad column name reported CORRUPTION for a broken test; and the first clean run did not actually overlap,
which the overlap guard caught and REFUSED to count, fixed by warming the ingest loop rather than lowering
the bar. | this commit | 2026-07-28
phase2 | GATES B, C, AND D RUN ON SYNTHETIC FIXTURES, instruments built first and each proven by a positive
control before its first counted run | Evidence under docs/evidence/phase2/.
GATE B, tools/verify/stress, new. Race at 8 AND 32 both elect exactly one winner. Load 20s: 19,980 samples,
999 rps, p95 10.5ms, p99 12.7ms, zero errors against targets of 300ms and 800ms. Soak: p95 10.4ms, flat
against the load run. CONTROLS: the race control's first planted defect (dropping FOR UPDATE) still produced
one winner because the timing did not interleave, which proved the harness ran and not that the assertion
detects, so it was made DETERMINISTIC and then caught 8 winners; the load control used an unmeetable 1ns
target and was caught. SCOPE LIMIT STATED: load and soak measure the DATABASE path, not authenticated HTTP,
because per role fixture sessions do not exist yet. Measuring what can honestly be measured beats reporting
an HTTP number from a harness that logged in as nobody.
GATE C, tools/verify/visual-sweep, new. 44 captures across 9 routes and 5 viewports as a real logged in
role. CONTROL: a planted 4000px element was DETECTED. THREE INSTRUMENT DEFECTS fixed before the counted run:
it logged in once per role PER VIEWPORT and the auth limiter correctly throttled it; it pointed at
127.0.0.1 so the browser Origin failed the CSRF allowlist; and QUIC over the tailnet produced transport
errors that surfaced as 41 phantom findings, so pinning the transport dropped the count from 78 to 37.
Reporting 78 would have been reporting the instrument.
THE REAL GATE C FINDING, AND IT IS A FAILURE BY THE PROTOCOL'S OWN STANDARD: the missionMATCH application
does not fit a phone. All nine routes overflow at 320 and 390; minimum content width is about 732 pixels;
768, 1024, and 1440 are clean. The cause is structural, a fixed sidebar desktop shell. It is RECORDED, NOT
SILENTLY FIXED, because rewriting the app shell's responsive behaviour is not Phase 2 scope and doing it
mid gate is exactly the unscoped drift the protocol prevents. Whether it matters is a product judgement:
if phone support is not a requirement for an internal desk tool, the honest resolution is to record that and
narrow the Gate C viewport set, rather than carry a permanent failing assertion nobody intends to fix.
THAT DECISION IS JOSEPH'S. No baselines established: the protocol says baselines follow his approval of the
contact sheet, so the pixel regression half of Gate C cannot run until one exists. Stated rather than
claimed.
GATE D, drift sweep: engine diff EMPTY against 9e77f65 with a positive control proving the detector sees a
real diff elsewhere; zero Python with a planted file detected and then gone; zero dashes across four
authored docs; versions queried PER DATABASE; zero secrets in tree and history; five new tenant tables all
forced RLS with organization_id; architecture document tracked and secret free. EXPECTED DRIFT NAMED:
staging is v81 while tenant and control plane are v80, because 0081 is staging only pending an approved
deploy. | this commit | 2026-07-28
phase2 | GATE A OPEN, PENDING EXACTLY ONE BLOCKER | Every synthetic half of Gate A is proven and filed. What
remains needs a REAL SOLICITATION PACKAGE from Joseph, an RFP or combined synopsis with a PWS and at least
one amendment. docs/PHASE2_DOCUMENT_TRIAL.md is a READY TO EXECUTE checklist: seven ordered steps with the
assertion at each, deterministic extraction BEFORE any model call per Section 5.1, the external boundary
assertion that a tenant document must be structurally unreachable rather than merely unused, and the two
deferred Phase 1 items (intake screen 3 uploads, cmd/sam-attachment-sync) which unblock THE SAME DAY because
they were deferred on the same missing thing. One judgement call is pre recorded so it is not mistaken for a
failure: a real PWS will name skills the ontology lacks, and leaving them unlinked is CORRECT behaviour, a
curation backlog rather than a defect. | this commit | 2026-07-28
SESSION START DRIFT VERIFICATION, run and recorded BEFORE any change | Evidence in
docs/evidence/phase2/drift/session-start-drift.txt. HEAD 58a94a1, 365 commits, working tree clean.
NO UNEXPECTED DRIFT. Engine diff against 9e77f65 EMPTY, with a positive control proving the detector sees a
diff that does exist. Zero Python, with a planted file detected then confirmed gone. Zero dashes across five
authored documents. Zero key material in the working tree, in git history, and in the architecture document.
Build routes DARK in production: approve 404, scope 404, tracked 405 (the pre build shape), site 200. Merge
gate PASS. Schema queried PER DATABASE: control plane v80, tenant v80, staging v81. Both production nodes on
the identical binary 8f24414531e80a9a, matching the handoff. Six accounts, one operator, ALL SIX carrying a
real last_login, which is the Track H item 5 fix holding overnight.
THE ONE DIFFERENCE IS EXPECTED AND NAMED: staging is one ahead at v81 because migration 0081 is staging only
pending an approved deploy. The nightly backup timer has not fired yet, first firing 29 July 03:07, and
yesterday's manual runs are on disk with Result=success. Neither is drift. | 2026-07-28
PRODUCT DECISION RECORDED, THE SUPPORTED VIEWPORT FLOOR | Joseph, 28 July 2026. This is a RECORDED PRODUCT
DECISION, not a deferred bug, and the distinction is the point: a deferred bug is debt that accrues silently
and a decision is a boundary that can be checked.
THE DECISION, in three parts:
1. AUTHENTICATED SURFACES have a supported viewport FLOOR OF 768 for the duration of the design partner
program. missionMATCH is a desk and capture tool used at a desk. Below 768 is unsupported, not broken.
2. EVERY PUBLIC SURFACE keeps the FULL MATRIX, 320, 390, 768, 1024, and 1440. Candidates arrive on phones,
so the public careers pages and any other public surface hold the whole range with no relaxation. The
floor applies to the authenticated application and to nothing else.
3. THE PHONE CAPABLE APP SHELL enters docs/IMPROVEMENTS.md as the PRE APPROVED single improvement item for
PHASE 5, where the desk screens are being rebuilt anyway. The continuation protocol Section 4 item 4
permits at most one approved improvement per phase; this is Phase 5's, allocated in advance.
WHAT THIS CHANGES ABOUT GATE C: it does not lower a standard, it SPLITS the matrix by surface. The
authenticated sweep asserts 768, 1024, and 1440; the public sweep asserts all five. A single relaxed number
would have quietly excused the public pages too, which is the failure this split prevents.
STANDING GUARD RULE, effective immediately and asserted by the instrument rather than remembered: NO NEW
SCREEN MAY INTRODUCE FIXED WIDTH CONSTRAINTS BEYOND THE SHELL'S EXISTING ONES. The shell's floor is a known,
measured quantity (about 732px of chrome); a new screen that pushes the minimum higher is a regression even
at supported widths, and the visual sweep now measures the minimum content width per route and fails on any
route that exceeds the shell baseline. Without that, "we support 768" would decay into "we support whatever
the widest screen happens to need". | this commit | 2026-07-28
PHASE 2 GATES FILED AS ONE EVIDENCE BUNDLE | docs/evidence/phase2/, README states what it does NOT claim
next to what it does. Every leg re run at one commit so the bundle describes one state of the tree.
GATE A functional PASS. 52 packages 0 failing; the boundary suite is 20 tests of which five exist to make
the suite honest rather than to test a feature; the promotion race asserts its own
overlap (29 ingest rewrites during the race) so it cannot pass by not overlapping.
GATE A REMAINS OPEN on exactly ONE blocker, a real solicitation package, which is
Joseph's and is not technical.