Skip to content

DEM-PLACEHOLDER-GUARD (core): the terminal guard — a placeholder can never come back - #39

Merged
Gio2050 merged 4 commits into
mainfrom
dem/guard-core
Aug 26, 2026
Merged

Gio2050 merged 4 commits into
mainfrom
dem/guard-core

Conversation

@Gio2050

@Gio2050 Gio2050 commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Wave 4 of 4 — DEM-PLACEHOLDER-GUARD, step 1 of 2. Merge after all three producer waves. This is where it's finished.

The accepted row and gate

DEM-PLACEHOLDER-GUARD | D-DEM-4(5): the terminal CI guard, bounded to code literals in afi-core/analysts/** and afi-reactor/src/pipeline/nodes/** with the mapping-sourced exemptions stated there, plus removal of any residue of the retired constants under the D-FLPR-6 forward-only discipline | Guard proven to fail on a reintroduced literal by a negative test, and proven not to fire on a registered mapping's declared literals or on liquiditySwept; the D-DEM-4(2) inventory empty; zero scored-value movement by this slot alone; no compatibility shim, alias, dual-run mode, fallback flag, or commented-out copy remains

How it decides

The field set is read from the FroggyTrendPullbackInput interface itself, so the guard can never drift from the rubric's contract. A finding is an assignment to one of those field names whose value is a compile-time constant — and constancy is decided by the type checker, not only the syntax tree.

That last point is the whole reason this works. The retired stub at laneView.ts:79 was an identifier imported from another package, whose initializer lives in a .d.ts. A syntax-only scan cannot see that it is constant. The negative tests reconstruct exactly that form and prove the guard catches it, with the finding's reason naming the literal type.

Forms covered: literals, as const, unary of a literal, substitution-free templates, x ?? CONST, conditionals between constants, variable declarations, shorthand properties, assignment expressions.

The exemptions are exactly the ones the accepted text states — and no wider

  • liquiditySwept — the reserved two-lane computation (D-DEM-3(5)).
  • Test directories — fixtures legitimately build inputs by hand.
  • A registered mapping's declared default literals — pinned to the registered values, read from the document itself. So the two D-DEM-5(4) grandfathers the retired adapter still applies (its physical deletion is reserved by DEM-BIND ruling R1) cannot drift from the registered document, and any other literal at the same site still fires. A synthesis is caught even when one of its arms equals a registered default.

17 tests: the tree is clean; six negatives prove the guard bites on every retired form; five exemption tests prove it does not fire where the text says it must not.

typescript is imported lazily, so the published module is inert unless a test invokes it. Zero scored-value movement.

🤖 Generated with Claude Code

Gio2050 and others added 4 commits August 25, 2026 17:29
…eralize the composer

DEM-GOV §9 DEM-PRODUCER-PLAN (owner-authorized 2026-08-25; afi-governance
#55): the planned R:R is a PROVIDER fact the technical lane produces from the
submitted afi.trade-plan.v1 and the registered mapping binds — never
synthesized from analyst inputs again.

- froggy.enrichment_adapter.ts: the synthesis at the old :282-283 is deleted;
  the builder returns FroggyAdapterOutput (Omit<…,'rrMultiplePlanned'>); the
  view type gains technical.plan (the lane's verified plan facts, read only
  through the mapping).
- froggy.residual_builder.ts: residual Pick shrinks to the five still-
  unexpressible fields; the composer is mapping-version-agnostic and
  fail-closed — fragment ∪ residual must partition the ten scorer-input fields
  exactly and every value must lie in its declared domain (predicate-valued
  table: no scorer-input field is ever assigned a literal here).
- froggy.trend_pullback_v1.ts: scoreFroggyTrendPullbackFromEnriched (the
  adapter-only convenience path) is deleted — the adapter no longer emits a
  full input; the rubric no longer imports the adapter.
- tests: the registered 1.1.0 mapping as shared test data (inline, drift-
  checked against the sibling registry file when present); the residual
  builder suite proves compose(fragment, residual) == adapter output + the
  mapping-bound R:R (fired default recorded when the producer emitted none);
  composer refusal cases (missing / extra / dual-source / out-of-domain).

No scoring-law value moves. afi-core suite 312/312.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ed literal

DEM-GOV §9 DEM-PRODUCER-CANDLE (owner-authorized 2026-08-25; afi-governance
#55) — the act D5-GOV D-D5-1 reserved. Merge after dem/plan-core, and after
the reactor's PLAN wave has merged.

- froggy.enrichment_adapter.ts: BROKE_EMA_WITH_BODY_UNIMPLEMENTED_STUB and its
  ?? read are DELETED, as is the `haFlatBackConfirmed: false` literal. Both are
  now computed technical-lane facts the registered mapping binds (required).
  FroggyAdapterOutput drops them; the view type gains the lane's
  brokeEmaWithBody / haFlatBack / haFlatBackConfirmed facts.
- froggy.residual_builder.ts: the residual Pick shrinks to the HTF bias
  placeholders + liquiditySwept — three fields.
- tests: mapping 1.2.0 in the shared support module (required binds); probes
  carry the candle facts and vary them; a view WITHOUT them now REFUSES at the
  interpreter (required-source-absent) instead of taking a stub — pinned in
  both the residual-builder and adapter suites.

afi-core 313/313. No scoring-law value moves.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e "unknown" branch

DEM-GOV §9 DEM-PRODUCER-HTF (owner-authorized 2026-08-25; afi-governance #55)
— the mission DIR-GOV D-DIR-3's scope-guard reserved. Merge after the CANDLE
wave.

- froggy.enrichment_adapter.ts: the `weeklyBias = "neutral" as const` and
  `dailyBias = "neutral" as const` literals are DELETED. Both are now computed
  technical-lane facts (higher-timeframe trend) that the registered mapping
  RECODES into the rubric's bias vocabulary. The view type gains technical.htf
  {daily,weekly}. NOTHING this builder returns is a placeholder any more.
- froggy.residual_builder.ts: the residual is now ONE field, liquiditySwept —
  the two-lane read D-DEM-3(5) expressly reserves. The D-DEM-4(2) placeholder
  inventory is EMPTY.
- froggy.trend_pullback_v1.ts: the "unknown"-direction branch semantics are
  RESOLVED as DIR-GOV's scope-guard requires (they could not be settled while
  both biases were hardcoded and the branch unreachable): with real HTF facts
  the four cases are exhaustive — long/short when both timeframes agree,
  neutral when either has no directional bias, and "unknown" when both are
  directional and DISAGREE (a higher-timeframe CONFLICT: the analyst declining
  to call a direction, not an error and not an absence). It feeds no axis and
  never reaches scoredSignal.direction. No code changed — the branch is
  documented and pinned.
- tests: mapping 1.3.0 in the shared support (the bullish->long / bearish->short
  / range->neutral vocabulary translation lives in the MAPPING, never in the
  producer, D-DEM-3); probes for aligned / conflicting / range / absent-window
  HTF; five new direction-verdict tests including the conflict branch and a
  proof that the verdict moves no axis.

afi-core 322/322. No scoring-law value moves.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…never come back

DEM-GOV §9 DEM-PLACEHOLDER-GUARD (owner-authorized 2026-08-25; afi-governance
#55), enforcing D-DEM-4(5). Merge last, after all three producer waves.

- validators/PlaceholderLiteralGuard.ts (NEW): reads the field set from the
  FroggyTrendPullbackInput interface itself (so it can never drift from the
  rubric's contract) and flags any assignment to one of those field names whose
  value is a compile-time constant. Constancy is decided by the TYPE CHECKER as
  well as the syntax tree, so the exact retired form — a constant imported
  ACROSS a package boundary, whose initializer lives in another repo's .d.ts —
  is caught; a syntax-only guard cannot see it. Forms covered: literals, `as
  const`, unary of a literal, substitution-free templates, `x ?? CONST`,
  conditionals between constants, variable declarations, shorthand properties,
  and assignment expressions. The `typescript` API is imported LAZILY, so the
  published module is inert unless a test invokes it.
- Exemptions are exactly the accepted text's: `liquiditySwept` (the reserved
  two-lane computation, D-DEM-3(5)); test directories; and a registered
  mapping's declared `default` literals — pinned to the REGISTERED VALUES, so
  the two D-DEM-5(4) grandfathers the retired adapter still applies (its
  physical deletion is reserved by DEM-BIND ruling R1) cannot drift from the
  document, and ANY other literal at the same site still fires. A synthesis is
  caught even when one arm equals a registered default.
- test/guardrails/placeholderLiteralGuard.test.ts (NEW, 17 tests): the tree is
  CLEAN; six negative tests prove the guard bites on every retired form,
  including the cross-package one; five exemption tests prove it does not fire
  on mapping-sourced values, a mapping document's own literals, liquiditySwept,
  test fixtures, or the composer's predicate table.

Zero scored-value movement by this slot. afi-core 339/339.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kilo-code-bot

kilo-code-bot Bot commented Aug 26, 2026

Copy link
Copy Markdown

Kilo Code Review could not run — your account is out of credits.

Add credits or switch to a free model to enable reviews on this change.

@Gio2050
Gio2050 merged commit c21946d into main Aug 26, 2026
1 of 2 checks passed
@Gio2050
Gio2050 deleted the dem/guard-core branch August 26, 2026 04:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant