Skip to content

Fulcrum v3.0.0

Choose a tag to compare

@oernster oernster released this 26 Jul 15:28
· 147 commits to main since this release

Release notes

IMPORTANT NOTE:

  • macos dmg release asset removed since I forgot to notarise it (I didn't know that step was necessary after developer signing; the latest version has been notarised).

What's new

Three changes define this release. Fulcrum can now model the disease the
books call most interesting: matrix and dual-reporting structure,
represented as authority claims and priced by the same deterministic engine
as everything else. The guide became a plan for every level of the
organisation at once, honest about what composes and what merely looks good
from altitude. And the session became a record: move history survives
closing the app, undo reaches across runs and the exported report covers
everything ever played. Alongside them, the site gains a page that
publishes every scoring coefficient with its provenance.

  • Authority claims. A claim records another actor asserting the right to
    decide for a team: a second reporting line, a functional chapter, a matrix
    overlay. The claimant can be a team, a unit at any level or a plain label
    for someone not modelled at all (a "Head of QA" need not sit on the
    delivery graph to contest a decision class). A team's own authority is
    never a self-claim; Ships without asking stays the single source of truth.
  • Contested ownership is priced, three ways. Every decision class already
    has a structural owner (the team itself, or the line it escalates to), so
    any standing claim makes its subject contested: who decides must be settled
    before anything can be decided. A contested team's capacity takes a
    dedicated penalty that is validated never to be gentler than clean
    escalation, it counts in the escalation share and the whole score divides
    per standing claim. An org without claims scores exactly as before, pinned
    by a golden test over the ten example archetypes.
  • New moves, from great repair to canonical blunder. Resolve authority
    collapses a contested class into a single accountable owner, in the team's
    favour or a claimant's, either way leaving one resolvable worldline.
    Downgrade a claim turns a claimant into a consulted party: an explicit
    dependency with a priced delay, an interface rather than shared control.
    Impose a matrix overlay is the new standing blunder beside the approval
    layer: a second claimant lands on every team at once, so the falsifiable
    claim has a twin (model a realistic org where dual reporting improves
    structural health and the framework is wrong). A claimed team never gets a
    plain delegate move, because granting authority without settling the claims
    adds a claimant instead of removing one.
  • Contest is visible everywhere. A fifth watched signal (Contested
    ownership) counts claimed teams; contested nodes border red on the org map,
    the complete picture and the exported SVG, with "contested" in their
    status line; a unit rolls up how many of its teams are contested; the
    glossary explains claims in plain language with the same tooltip source as
    the rest of the app.
  • Claims in the editor and on disk. The organisation editor gains an
    authority-claims table beside the dependency table: pick a claimant (any
    team or unit) and the team whose decisions it claims, with illegal rows
    flagged in red and ignored rather than blocking OK. Claims round-trip
    losslessly through Edit my org, the autosave and JSON import and export,
    imported free-text claimants included; older files without claims load
    unchanged.
  • The guide now plans every level at once. "Show the guide" opens a
    two-pane tree of the whole hierarchy: every leaf row is badged with its
    line's worth in whole-org points (the honest currency: the composing
    badges sum to the headline), while a frame's own 0 to 100 climb appears only in the
    detail pane with its scale stated plainly, so a section's private "50 →
    99" never reads as a promise the whole organisation cannot keep. Leaf
    lines act on real teams and compose, so the header states an honest
    whole-org before and after from playing every leaf line; an aggregate row
    is labelled as the view from that altitude and never counted toward the
    headline, because its gains overlap the leaf repairs beneath it. Planning
    runs on a worker thread, every step states its before and after score on
    the frame's scale and any step previews and plays in place. The
    grow toggle prices growth where the edges it relieves are visible: a
    whole-organisation growth row planned against the real organisation from
    the position after every leaf line, appended as the tree's last
    composable row with its badge being growth's worth on top of the other
    lines, plus split and add moves inside aggregate frames for the real
    teams standing in them (a loose team at the top level), since those
    frames are where cross-unit edges are priced; when growth improves no
    line anywhere the guide states that plainly and a frame whose own line
    growth cannot improve says so in its detail pane, so a dependency-free
    section never reads as a dead toggle. The planner also never
    names the identical move twice in one line: replaying a partial repair
    converges with diminishing gains and read as noise.
  • Every team now counts in the guide. A team held directly by a unit
    whose child units also hold teams used to vanish from the plan entirely:
    no aggregate view rolled it up and no leaf line contained it, so its
    repairs never reached the headline. Such teams now stand as real nodes in
    their unit's view and get their own composable "Teams directly in" row
    (the top level gains one for its loose teams), restoring the rule that
    every team sits in exactly one leaf row. Their claims travel with them,
    so a contested direct team is never offered a delegate that would bypass
    the contest.
  • A leaf line composes only when it pays its way. A line that is this
    frame's own best play can still cost the whole organisation once composed
    (merging healthy teams raises the weight of every problem elsewhere), so
    composition now prices each line against the composed position and leaves
    a net-harmful line out of the headline, keeping its row and badge but
    flagging it as not composing with its cost stated. The header says
    plainly when any line was left out.
  • Stabilise interfaces is now scoped to its frame. The move carries its
    frame's nodes and thins only the edges that frame prices: played inside a
    unit it steadies that unit's interfaces; played at the top level it
    steadies the boundaries between units, unit-level dependencies included.
    Previously it thinned every dependency in the organisation from any scope.
    Saved plans replay unchanged (an untargeted move keeps the old meaning).
  • The move column runs the full height of the board. The available-moves
    list now starts at the top of the window, headed by the Play this level
    button, so more moves and signals are visible without scrolling; the score
    block sits with the map on the left and the keyboard ring follows the new
    reading order. Every splitter in the app now draws one standard visible
    divider colour with clear space either side and both scrollbar
    orientations match it, so no native light strip breaks the theme.
  • Move history survives closing the app. The autosave now records the
    starting organisation and every move beside the current org (older saves
    load unchanged), and the next launch rebuilds the session by replaying
    them, so Take a move back walks through earlier runs' moves too, all the
    way to the original organisation. Every play and take-back saves
    immediately, so the record survives however the app ends. The presentation
    covers the whole record with earlier runs' moves marked apart from the
    current run's by colour (a grey rail against an amber one, with a legend
    and counts), it is written straight into the Downloads folder under a
    fresh name with no save dialog and creating it is available whenever any
    move exists in the record, not just after a move this run. Undoing a
    historic move rewrites the record: it stops being an earlier run's move.
    Replacing the organisation (new random, wizard, import or an edit) starts
    a fresh record for the new structure; an imported plan's moves arrive as
    its record. Every move is named at the moment it is played (against the
    organisation it acted on, since a later collapse can rename its targets)
    and the note under the map now reads "Last move played: ..." with that
    name, adding "(in an earlier run)" when the move is restored history, so
    it always says exactly what it explains.
  • The complete picture routes its dependency lines cleanly. An edge
    whose straight line would cut through unrelated boxes now leaves its
    source's right side, runs down its own clear lane beyond the diagram and
    enters its target from the side with the arrow, while an unobstructed
    edge keeps its straight line; nothing is ever drawn through a box it has
    nothing to do with. Where one line crosses another, the horizontal run
    hops the vertical with a small semicircle, the circuit-diagram convention
    for wires that do not meet, and a box with several connections fans them
    out along its edge (ordered by where each counterpart sits) so parallel
    runs never overlap. A hub with more connections than its edge can present
    merges them into one counted trunk per direction ("× 10" into the box on
    a single shared lane), so neither the box edge nor the lane gutter ever
    drowns in lines.
  • The installer hands the app the foreground again. After launch on
    finish, the installer now waits for the app's window to appear and fronts
    it before closing itself; previously the window could arrive after the
    installer had gone and Windows left it flashing on the taskbar.
  • A drill crash fixed. Focusing a unit whose child units hold no teams
    used to build an empty frame; such a unit now correctly plays as a leaf,
    with one shared rule deciding aggregate-ness everywhere.
  • The scoring model, published and grounded. The site gains "What grounds
    the score" (model.html): every coefficient in the scoring function with its
    provenance, the honest split between theory-derived mechanisms and
    hand-tuned magnitudes, the assumptions the model rests on, where it is
    expected to be wrong and the external blind protocol that would actually
    falsify it. The why page now distinguishes reproducibility from validity
    and names the archetype numbers as a consistency check on the
    implementation rather than proof about reality.
  • A sensitivity sweep, run and published. A repository script
    (sensitivity.py) perturbs every scoring coefficient by 0.8 and 1.2 and
    re-scores the ten example archetypes; all five qualitative conclusions
    (both archetype orderings, the typical-versus-well-designed gap and both
    blunders staying negative) survive every perturbed configuration. The sweep
    earned its keep during this release: it falsified the first cut of the
    contest semantics, under which the matrix overlay read positive on
    escalation-heavy archetypes, and the corrected rule (any standing claim is
    contest) is recorded on the model page rather than erased.

Existing JSON imports load unchanged. Scoring without claims is untouched: the
same model produces the same score as 2.0.0.

Licence

Dual-licensed by component: the model under GPL-3.0 and the user interface under
LGPL-3.0. Both texts ship with the app and are shown under Help.