Empty, NUL and unreadable paths, malformed UTF-8 and lone surrogates refuse
with the command's exit 1. The manifest is bounded (1 MiB, 1000 ships) and
duplicate JSON properties are refused. Titles refuse format, line and
paragraph separator characters and edge whitespace. Naming an already
retired key is a recorded no-op, so a rerun converges.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stryker flagged ParseManifest, ReadString and RunAsync as compile errors in safe
mode (out vars and try-assigned locals), leaving them unmutated. Behaviour unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ConceptSubjectRegistry is the only writer of ConceptSubject. ConceptKey is the
one key grammar (anchored with \z), shared with ConceptAppraisalService.
Not yet built or tested at this commit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The snapshot is regenerated; EF also reordered two pre-existing properties
(BridgeAction.TargetSurface, one ProjectId) into its canonical order.
Migration discovery now lists thirteen ids.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ConceptAppraisalService is the only appraisal writer; tallies are derived from
rows. VoterIdentity.For decides voter key and class; NetworkPrefix is the only
prefix computation (HMAC tag). NetworkTagSecret is required in production.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
concept-mill-votes:cut_report:cut-1.h1 and h2, held by Soul
(concept-mill-votes:verdict:cut-1.s2). Production refuses to start without
DataProtection:KeysPath; the deploy (Cut 0b) must set it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UseForwardedHeaders runs first with KnownProxies exactly loopback and
ForwardLimit 1; Data Protection always uses application name "Bifrost";
the startup validator refuses production without DataProtection:KeysPath.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Concept Mill votes Cut 0a (concept-mill-votes:cut_report:cut-0a.h1), held by
Soul (concept-mill-votes:verdict:cut-0a.s1). daedcc4, the live release, is now
an ancestor of main.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Imagination pass that admitted cut specs 0a through 7. Facts re-probed at
Bifrost bf83a93 and on the host; the from-memory prior-art list is replaced
by a citation of docs/research/anonymous-vote-stuffing-prior-art.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Served concept-mill-votes Cut 4 (anti-stuffing defence). Facts with source
pointers on PoW captchas, device cookies, network buckets, ePrivacy status of
anti-abuse cookies, and after-the-fact purge at HN, Stack Exchange and Reddit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Imagination map of 2026-10-08, committed as the target document of the
concept-mill-votes campaign. Typed invariants, rulings and cut specs live in
the eureka mind.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Eureka idunn-watchdog cut bifrost-notice-retry, attempts 1-2. Soul verdict
idunn-watchdog:verdict:cut-bifrost-notice-retry.s2 holds. Retries with the
same nonce after 1,2,4..32 then every 60 minutes; the unit fails from the
third attempt while retrying (ruling notice-retry-policy).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A failed entry stamped ahead of the clock (host clock stepped forward then
corrected) is eligible now instead of waiting out the skew; the next attempt
rewrites the stamp. The WATCHDOG_NOTICE_NOW_MS env var is deleted: the reader
reads Date.now(), and tests fix it with a --import preload (tests/fake-clock.mjs).
The unknown-entry tests now rerun far past the backoff so a retried unknown fails.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The finally-sends test expected six attempts on all three journal rows, but the
closure entry is first tried in the run that completes its opening, so it has
one. Test-only fix; the previous commit's test failed on this line.
The previous commit (69e12e3867) builds; its
suite has one failing test, fixed here.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Ruling notice-retry-policy. The five-attempt cap and its terminal
"exhausted" state are gone. A failed entry is eligible again once
min(2^(attempts-1), 60) minutes have passed since its last journal
update; a run inside the delay skips it without spawning the bridge.
Every attempt keeps the same nonce. The exit status is non-zero while
any entry is unknown or has failed 3 or more times, and retries go on.
status shows nextEligibleAt for failed entries.
The clock is one now() that WATCHDOG_NOTICE_NOW_MS can override, like
the bridge timeout override, so tests drive the backoff without sleeping.
The process summary field "exhausted" is renamed "failing". A journal
entry whose updatedAt is not a date is now refused as
journal-entry-invalid, since the delay is measured from it.
All commits build.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Eureka idunn-watchdog cut bifrost-notice-reader, attempts 1-3 (spec r3).
Soul verdict idunn-watchdog:verdict:cut-bifrost-notice-reader.s3 holds.
One opening and at most one closure DM per incident, own journal, nonce
with enforce_nonce, unknown never re-sent. Requires CultLib >= 8cb3b728.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tests only, plus wording. No change to what the reader or bridge does.
- A spawn timeout and a spawn error (ENOBUFS from a bridge that floods
stdout) record unknown, are never re-posted, and block the closure.
WATCHDOG_NOTICE_BRIDGE_TIMEOUT_MS overrides the 60 s timeout so the test
reaches that branch; the default is unchanged.
- A 500/502/503/429 answer to the message POST exits 75 from the bridge, and
through the reader is retried with the same nonce until it completes.
- allowed_mentions {parse: []} is asserted in the posted body; status exits
non-zero while an entry is unknown and zero when none is.
- The bridge comment and docs/watchdog-notice.md no longer call a non-2xx
answer "definite": it is retried with the same nonce, relying on
enforce_nonce inside Discord's window, and may double-send if Discord acted
on a 5xx outside it.
Builds. Two tests fail on Windows (SIGKILL status), as before; they pass on
Yggdrasil.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Fix batch for Soul pass s1 (spec r3):
- Delete prune-on-absence and its test. Absence from Idunn's store no longer
deletes a journal entry, so an empty or restored store never re-sends.
- discord-dm exits 75 only when no message can have been sent (arguments,
token, DM channel open, non-2xx on the message POST). The reader records
failed for 75 only; any other non-success without a message id is unknown
and is never re-posted.
- Subject grammar is Idunn's require_id; the notice puts it in backticks.
- Times are bounded to the Date range; notice text is built before the
running entry is written.
Builds and passes on Linux. On Windows the two tests that SIGKILL the reader
cannot pass (process.kill does not deliver a signal there).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The egress test searched for the substring CANARY, which the recipient double
also carries, so it failed on its own input.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The top-level await ran main() before the const declarations below it, so every
process run died in the temporal dead zone with unexpected-error.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
tools/watchdog-notice.mjs is a one-shot reader. It reads Idunn's incident
store unlocked with SingleFileMessagePackBackingStore.pullAll, sends one opening
and one closing DM per incident through bifrost-bridge.mjs, and keeps a
bifrost.watchdog_notice_execution.v1 journal: a running entry becomes unknown
and is never re-posted, a failed one is retried with the same nonce up to five
times, terminal entries are pruned once Idunn retires the incident. The exit
status is the delivery-failure signal.
discord-dm accepts --nonce (at most 25 chars) and sends nonce with
enforce_nonce so a retry inside Discord's window returns the original message.
The tests run the CLI against the store bytes Idunn wrote (fixture from
GameCult/Idunn 7f7528b3) with a counting bridge double, and once through the
real bridge with fetch stubbed. docs/watchdog-notice.md states the CultLib
>= 8cb3b728 floor.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Eureka idunn-watchdog cut bifrost-retire-alarm, attempts 1-2. Soul verdict
idunn-watchdog:verdict:cut-bifrost-retire-alarm.s2 holds; the pump refuses
any verb but discord-post with a failed receipt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The two fixture tests forced a workstation-only target dir
(C:\Users\Meta\.cargo-target-codex); the child now inherits process.env.
Follow-up to 868df354, which missed this deletion. Builds.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
CultLib 8cb3b728 renamed only cultcache-ts to @gamecult/cultcache-ts; the
suite and three tools still required the bare name. Rename the require
string in tests/persona-feedback-cli, persona-discord-delivery,
persona-discord-crossing-rust-smoke, tools/persona-feedback (:114),
agent-transport and governance-threads. No fallback: a pre-8cb3b728 CultLib
fails loudly at import. operator-notification.mjs is untouched (retire cut).
The two Rust fixture tests skip, naming the missing producer (Epiphany
removed both fixture bins in 387afe49), and drop the hard-coded
CARGO_TARGET_DIR override. Builds.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
With discord-dm gone the verb set, listCommandRecords filter, resolveBridgeVerb
throw and the verb variable decided nothing, and the filter hid non-discord-post
commands so they stayed pending with no receipt. Delete all of it: a command
whose verb is not discord-post now reaches processOneCommand and is refused
with a failed receipt (error names no input value). Payload staging moves
inside the try so a refusal cannot escape as a thrown error.
Adds tests/cultmesh-bridge-commands.test.mjs: discord-dm refused with failed
receipt and non-ok result, canary-free error, discord-post still reaches the
actuator.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Delivery-crossing ruling: Bifrost reads Idunn's incident projection with its
own delivery journal, so Idunn no longer publishes alarms into Bifrost.
- Delete tools/operator-notification.mjs (259 lines); its only callers were
gamecult-ops notify-idunn-operator-alarm scripts.
- cultmesh-bridge-commands.mjs: drop discord-dm from supportedBridgeVerbs and
the recipient-id arm; --channel-id is always passed.
- bifrost-bridge.mjs discord-dm is unchanged (docs/bridge.md, BridgeCliTests).
Builds: yes.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
REC-099: `postBridgeJson` (tools/bifrost-bridge.mjs) and
`postDispatchRunJson` (tools/dispatch-agent-requests.mjs) each occur
exactly once in their file: their own definition, with no caller.
Each was the sole caller of that file's private `ensureTrailingSlash`
copy, which becomes dead in the same cut. The `ensureTrailingSlash`
copies in agent-transport.mjs and governance-threads.mjs are separate,
still-called definitions and were left untouched.
Kill attempts that failed: none.
- `rg -n "postBridgeJson" tools/*.mjs` -> only its own (now removed)
definition.
- `rg -n "postDispatchRunJson" tools/*.mjs` -> only its own (now
removed) definition.
- `rg -n "ensureTrailingSlash" tools/bifrost-bridge.mjs` and the same
against tools/dispatch-agent-requests.mjs -> confirmed each file's
copy had exactly one caller (the function just removed) before
cutting it.
Checks run: `node --check tools/bifrost-bridge.mjs` and
`node --check tools/dispatch-agent-requests.mjs` -> both syntax OK. No
package.json/test suite exists for these standalone tool scripts.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>