Skip to content

v4.0.0

Choose a tag to compare

@dong845 dong845 released this 26 Jul 01:05

Why a major bump. render_deck.py --deliverables now REFUSES to run until the deck carries a
.deck-gates.json recording that its quality gates actually ran. Anything scripting --deliverables
breaks until it writes that file (or a written waiver). That is the breaking change; everything else
below is additive.

Added — review effort is a tier the user picks, not one the skill infers

  • The rule that scales the loop ("scale the critic to stakes") always existed but was never offered.
    Measured on one low-stakes deck: ~32 subagents and ~2M tokens across research + build + two
    review rounds, with the user given no visibility and no choice. Step 0 now collects a one-word
    review: tierfast | standard | thorough — sizing BOTH cost centres, the research
    fan-out and the review panel (they measured comparable: ~1.02M tokens of research against ~0.95M
    of review).
  • standard and thorough are pure ALIASES for the low-stakes and high-stakes behaviour that
    already existed — every cell of the tier table either restates the stakes section or defers to it.
    A deck whose user says nothing behaves exactly as before. fast is the one genuinely new band
    and is opt-in only, never derived: one generalist critic and one round is a real recall drop,
    so it has to be asked for. Purpose decides the tier; SIZE never lowers it.
  • Floors no tier may move: the build-time geometry gate, the render lint, PRE-FLIGHT 12, at least one
    independent critic, the EXISTENCE of the primary-source gate, and .deck-gates.json. Per-section
    panels on large decks and corroborated consent on high-stakes decks are shapes, not weight, and are
    unaffected by the tier. fast may not silently ship over a revise — it returns to the user.
  • The hand-off now carries review: and cost: lines. A dial whose bill is never shown builds no
    intuition, so the next deck's choice is as blind as the last one's.
  • Pass the approved claim ledger to critics WHOLE, never a summary retyped for the dispatch. The
    largest measured saving here: 7 of 8 "unsourced" findings in one round-2 review were briefing
    artifacts, and the round they consumed was pure waste.
  • An earlier draft was rejected by a six-dimension adversarial audit (9 blockers, 27 majors) and
    largely rewritten. Two invented optimisations were dropped rather than patched — "triggered
    arbitration" at standard (it contradicted four surviving statements that low-stakes dispatches no
    arbiter, including agents/arbiter.md's own brief) and narrowing round 2 to the changed slides (the
    review-validation gate's scope buckets are whole-deck and per-section only, so a scoped round 2
    bounces and costs an extra round). Both are recorded in critic-panel.md as deliberate reversions
    with their reasons.

Added — the hand-off gate, and a consent record that is evidence rather than a claim

  • --deliverables refuses without .deck-gates.json: a critic verdict, the design plan's
    boldness / signature_move / carried_by / form_ledger, and the provenance pass's per-claim
    claims list (a summary tally is rejected on purpose — a tally is written by the same pass that
    would have skipped the refutation). Any gate deliberately skipped is waived in writing, and the
    tool prints the reason, so a skip is visible instead of invisible.
  • validate_review.py --record <deck-dir> writes the critic block from the validated review
    itself
    — verdict, blocker/major counts, the review file's path and sha256 — and --deliverables
    re-reads that artifact. A record the model types at hand-off is self-certification: the model that
    skipped the loop writes the same JSON as the model that ran it. Verified by execution across five
    adversarial paths (review edited after recording → sha256 mismatch; file moved; record says consent
    while the review says revise → "the artifact wins"; a review consenting with a blocker still open;
    hand-written record with no source → still passes, labelled SELF-REPORTED). Backward compatible by
    design: the old shape is not broken, it is made distinguishable.

Security — credentials are scanned by SHAPE, and the recurring sk-* false positive is documented

  • A third-party multi-engine scan reported a possible hardcoded sk- API key. Audited the full tree
    and the full history (1,645 blobs, all refs): no credential exposure and nothing to revoke.
    Every hit is a CSS class name — the direction-gate preview names its classes after slide skeletons
    (sk-body, sk-split, sk-island, sk-band, sk-rail, sk-statement), and sk- is short for
    skeleton. A real key is sk- + ~48 high-entropy characters; these are sk- + four to nine
    lowercase letters.
  • This was the second time that report had been filed and dismissed by hand (v3.9.0 was the first).
    The false positive is not the expensive part — a report that is always wrong teaches everyone to
    ignore the scanner, and then the one true positive is ignored too.
    So the dismissal is now a
    command: scripts/scan_secrets.py discriminates by minimum length AND a Shannon-entropy floor
    rather than by prefix, covering OpenAI, Anthropic, GitHub classic + fine-grained, AWS, GCP, Slack,
    private-key blocks and generic secret = "..." assignments. It never prints a matched value —
    only its location and a masked head — because a scanner that echoes what it found has published it
    into your CI logs.
  • It self-tests both directions before it scans (real shapes must be caught, this repo's own
    strings must not be), so a rule that silently stops matching fails the build instead of reporting
    clean. Wired into CI on every push and PR: selftest → working tree → every blob in every commit
    (a key deleted in a later commit is still leaked, so the tree alone is not an answer).
  • .gitignore now covers .env*, *.key, *.pem, *.p12, id_rsa*, *_secret* and
    credentials.json — staying out of version control becomes a mechanism rather than a habit.
    image-generation.md no longer shows any literal after OPENAI_API_KEY=; the documented pattern
    reads from a local key file, because a scanner cannot tell a placeholder from a credential and
    neither can a reader skimming a diff. New SECURITY.md records the audit and the ten-second check.

Added — a CI guard that a SKILL.md slimming layers content instead of deleting it

  • scripts/check_skill_lossless.py + a CI step prove every substantive line of a baseline SKILL.md is
    still findable somewhere in the skill tree after a refactor. Deliberate deletions go in an allowlist
    keyed on a hash of the normalised line, so editing a line revokes its waiver.

Changed — SKILL.md layered into step-scoped references (228KB → 134KB), losslessly

  • Working detail moved into eight new step-scoped reference files, each with a live trigger at the
    pipeline moment that needs it. Two files were deliberately pulled BACK inline — the deckkit
    component catalogue and the render self-check — because their absence makes no noise: no lint fires
    when you hand-roll a form the library already has or skip the scan entirely, so a rule whose omission
    is silent cannot live behind a read. Verified 2298/2298 lines accounted for.

Fixed — native_chart(value_fmt=...) now rejects the wrong format dialect

  • value_fmt is written straight into the chart's EXCEL number-format code, while iso_bars takes the
    Python dialect, and neither docstring said so — a {:,.0f} handed to native_chart was printed raw
    onto a shipped slide. It now raises a named ValueError. Pre-existing, unrelated to the refactor.

Added — TITLE-RULE MONOCULTURE lint (the title-chrome rotation rule is now GATED, not prose-only)

  • The render self-check has long said "the title CHROME is not one fixed template repeated on every
    slide — rotate 2-3 treatments", but it lived only in prose and got missed (a head()-style build
    helper that stamps one eyebrow+rule on all 12 content slides is exactly how it regresses; a critic
    pass didn't catch it either). scripts/lint_deck.py now detects it deterministically: a per-slide
    title_rule_y feature + a deck-level check that fires TITLE-RULE MONOCULTURE when the same thin
    rule sits under the title at the same height on >60% of content slides (rules must CLUSTER at one
    height, so a deck that rotates rule/tab/rail/ordinal never trips it; filled eyebrow tabs and vertical
    accent rails are excluded by geometry). Mirrors the existing BOTTOM-STRIP MONOCULTURE gate. SKILL.md
    render self-check "Titles" now references the backstop. Verified: fires 10/10 on an all-rule deck,
    clean on a rotated one.