Reveal-gated canon (spoiler scoping) + continuity.premature-reveal check#2238
Merged
Conversation
…l check Canon context is injected into every issue's drafting prompt, so a withheld character secret or late-story twist could leak into the prose long before its reveal. Canon entries now carry optional reveal gating: revealIssue (hide before that issue), spoiler (hard-hide from all drafting), and surfaceDescriptor (the spoiler-free pre-reveal stand-in). buildStageContext and the alternate-POV rewrite cast filter canon to each issue's reveal horizon; the judge and editorial checks keep the full canon so they can verify hint placement. Absent gate fields = always visible, so existing entries and federation peers round-trip unchanged. Adds a reveal-timing editor + spoiler badge to every canon-entry card, and a continuity.premature-reveal editorial check (gated on any reveal-gated canon existing) that flags a gated fact leaked before its reveal issue, distinguishing a leak from deliberate foreshadowing. The reveal issue also feeds the Chekhov setups-payoffs check as an authored payoff. Migration 168 seeds the new stage prompt into existing installs. Closes #2178
…Part on premature-reveal Two contract fixes surfaced in local review: - chekhov.setups-payoffs now reads ctx.canon (reveal-gated payoffs), so 'canon' must be in its `sources` or an obsolete orphaned-payoff finding never goes stale after the reveal gate is edited away. - continuity.premature-reveal's buildVars never set finalPart, so the shipped prompt always rendered the "reading in PARTS" branch — even on a single-chunk whole-book run that has no setup digest. Mirror the sibling checks' meta.isFinal wiring.
…ting resolveCast only compared c.id to keepFullId, but the caller resolves the POV by id OR name (c.id === povCharacterId || c.name === povCharacterId). So a name-form rewrite request against a reveal-gated POV character in an earlier issue lost the narrator's full private canon (surfaced) or returned unknown-character (dropped). Match the same id-OR-name identity in the exemption. Adds a regression test covering the name-form gated POV.
e9e6395 to
ff4638c
Compare
…c test The 10→11 pipelineSeries bump (#2179) left dataSync.pipelineUniverse.test.js asserting a hardcoded senderV: 11 / receiverV: 10 for the "sender ahead" gate — but the receiver is now 11, so sender==receiver and the snapshot applied instead of blocking, reddening CI on main. Derive both versions from PORTOS_SCHEMA_VERSIONS (sender = receiver + 1) so this test stops drifting on every future bump. Dynamic-imported after the vi.mock hoist.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
CWQE Phase 13 (#2178). Canon context — including character secrets and late-story facts — was injected into every issue's drafting prompt, so the writer model could leak a mystery or twist issues before it was due. This adds reveal gating to canon and an editorial check that catches leaks that slip through.
revealIssue(int — hidden from any earlier issue),spoiler(hard-hide from all drafting context), andsurfaceDescriptor(the spoiler-free pre-reveal stand-in, e.g. "the locked east wing" instead of "the wing where the heir is imprisoned"). Sanitizer + Zod parity + client mirror. Absent fields = always visible, so every existing entry and federation peer round-trips unchanged.buildStageContext(textStages.js) and the alternate-POV rewrite cast (perspectiveRewrite.js) filter canon to each issue's reveal horizon — a gated fact is dropped or reduced to itssurfaceDescriptor. The judge and editorial checks intentionally receive the FULL canon (documented inseriesCanon.js) so they can verify hint placement. The POV narrator is exempt from gating (they know their own secrets), matched by the same id-OR-name identity the caller resolves the POV with.continuity.premature-reveal(series-scope, LLM, gated on any reveal-gated canon existing) scans drafted prose for a gated fact stated or unambiguously implied before its reveal issue, distinguishing a genuine leak from deliberate foreshadowing. Its reveal issue also feeds thechekhov.setups-payoffscheck as an authored payoff so an orphaned reveal is flagged.Test plan
server:npm test -- lib/storyBible.test.js lib/canonPrompt.test.js lib/editorial/checkRegistry.test.js lib/editorial/prematureReveal.test.js services/pipeline/textStages.test.js services/pipeline/perspectiveRewrite.test.js ../scripts/migrations/168-editorial-premature-reveal-stage.test.js— all green (includes reveal-filter behavior, surfaceDescriptor substitution, sanitizer round-trip, check gating, migration seed/idempotency, and the name-form gated-POV regression).client:npm test -- src/components/pipeline/CanonCard.test.jsx— reveal-timing editor + spoiler badge, green.Closes #2178