Repository navigation
Fulcrum v3.0.0
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.