North-star orientation — landed evidence
One cumulative record, updated as each node of the plan lands. Anchors below are stable; the plan's evergreen sections link to them as they collapse.
Declared measures and what each has produced
| Measure | Evidence | Verdict |
|---|---|---|
| Count discipline | Six entries write and read while the audit returns one advisory warning — § project state | pass |
| Optional everywhere | A project omitting the collection composes without gaining the key — § project state | pass |
| Dangling reference caught | An undeclared id returns a non-fatal diagnostic naming it, and the write still succeeds — § plan label | pass |
| Suite green | Full suite 1,731 passed / 0 failed at the integrated commit — § plan label | pass |
| Declared and rendered | Cockpit band renders each declaration in order with statement and live count; screenshot figure still outstanding — § prominence | partial |
| Plans point at them | Badge and single-select facet proven; labelling reckon's own plans is the remaining node — § prominence | partial |
| Orientation rollup | Per-direction plans, completion and remaining effort, with unoriented at info and undeclared at error — § orientation rollup | pass |
A north-star collection in project state
Commit cb230a2 — reckon/_schema.py,
reckon/project_state.py, tests/test_project_state.py; 147 insertions
across 3 files. Gate passed.
Each entry requires a non-empty id, name and statement of what winning looks like, and
permits an optional href for a direction that needs argument rather than a
sentence. Duplicate and unsafe ids are refused; entries round-trip losslessly beside sprints
and milestones, reusing the rollup machinery those two already have rather than introducing a
typed resource root.
The count cap is advisory by construction, which is the property worth recording: at six
entries against a cap of five the write succeeds and
audit_project_state returns one warning coded
north-star-advisory-cap-exceeded. A project drifting past a handful of directions
is reported, never blocked. A distributed project resource that omits north_stars
entirely reads and composes without the key appearing, so no project is required to migrate.
| Measure | Result |
|---|---|
| Scoped suite | tests/test_project_state.py tests/test_schema.py — 1,121 passed, 0 failed, 20.3 s |
| New cases | 6 parameter-expanded: round-trip, each required field, advisory warning without rejection, absence preserved |
| Worker time | 324 s measured from the run's own event stream |
A plan declares which direction it serves
Commit d7dfd50 — reckon/_plan_html.py,
reckon/_store.py, reckon/_schema.py,
docs/_shared/plan.schema.json, tests/test_mcp_store.py; 173
insertions across 5 files. Gate passed: 1,094 passed, 0 failed, 91.1 s, 4 new
tests. This run's scope was widened mid-flight and it is therefore excluded from calibration.
plan-north-star parses from semantic HTML into typed plan state, persists through
write and read, and appears in the regenerated public schema. The behaviour that matters is what
happens to a label the owning project has not declared: the read returns a non-fatal
diagnostic naming the unknown id, the write succeeds, and the label stays stored. Orientation is
a prompt to think about direction, so a dangling reference is surfaced without becoming a barrier
to writing — which is the locked unoriented-severity choice applied to the label as
well as to its absence.
| Measure | Result |
|---|---|
| Declared label round-trips | Read, write and meta round-trip preserve the label |
| Undeclared label diagnosed | Non-fatal undeclared-north-star warning names the id; write succeeds; label retained |
| Unlabelled plans untouched | No north_star and no validation_diagnostics keys appear, and a no-op write leaves bytes and version unchanged |
| Public schema regenerated | docs/_shared/plan.schema.json regenerated from the model, restoring committed-schema parity |
| Integrated suite | uv run pytest -q at the integrated commit — 1,731 passed, 0 failed, exit 0, 276 s |
The derived-artifact dependency is worth recording because it is not visible from the plan:
any change to the typed plan model invalidates the committed
docs/_shared/plan.schema.json, so a node touching the model must own the generated
file too or it cannot reach a green combined lane.
Directions reach the client, and the surfaces that show them
Commit 9ded8c0 — docs/ui/shell.jsx,
docs/ui/state-loader.js, reckon/serve.py,
tests/test_build.py, tests/test_crew_routes.py; 174 insertions,
3 deletions across 5 files. Gate passed: 14 passed, 0 failed, 4.3 s, 16 new
assertions; reckon build exit 0; ruff clean. Scope was widened mid-flight, so this run
is excluded from calibration.
The node's first act was to refuse to write. Given the cockpit, home and plan
components, it audited instead and reported that the deliverable was unreachable from them: the
served state carried no declarations and inventory rows carried no label, so no component could
render either. It cited shell.jsx:1042 rendering CockpitBody rather than
the assigned Cockpit, shell.jsx:134-256 and :950-977 owning
the plan-list facets and filtering, serve.py:529-584 and :598-607 omitting
the fields, and state-loader.js assembling window.STATE without them —
then left the worktree clean at its base commit. That audit was worth more than a partial
implementation would have been.
| Surface | Result |
|---|---|
| Served state | Discovery and state routes both returned the declaration and the inventory alignment |
| Cockpit band | Renders every declaration in declaration order with its statement and its live-work count |
| Live count basis | Counted over pending, active, in-progress and blocked plans — work in flight, not work finished |
| Plan badge | Title and plan-list rows name the resolved direction |
| Filter | One facet selects exactly one id |
| Optional everywhere | Zero declarations omit the band and the facet; zero labels omit both badges |
| No entry-point drift | 11 checked-in = 11 served = 11 built JSX scripts |
Two measures stay partial rather than passing. The declared-and-rendered gate asks for a banked screenshot, which needs the restarted server. The plans-point-at-them gate asks for all seven S7–S9 plans to carry a resolving label, which is the adoption node's work, not this one's.
The dependency scan reports how much work points at each direction
Commit d4d1502 — reckon/roadmap.py,
tests/test_roadmap.py; 185 insertions, 1 deletion across 2 files. Gate
passed: 25 passed, 0 failed, 1.0 s, 4 new tests, ruff clean.
roadmap gains a north_stars rollup — one declaration-ordered row per
direction carrying plans, completed, lifecycle completion percentage and remaining effort.
Declaration order rather than a computed ranking, so the reading matches how the project stated
its own priorities.
| Case | Result |
|---|---|
| Rollup figures | Three declared directions produced 2 plans / 1 complete / 50.0% / 3.0 remaining, 1 / 0 / 0.0% / 1.0, and 0 / 0 / 0.0% / 0 |
| A direction nothing points at | Still reported, with zeroes — an unclaimed direction is visible rather than absent |
| Unoriented live plan | Exactly one unoriented-plan finding at info severity |
| Draft and completed plans | Produce no orientation finding — only authorised, non-terminal work is asked to point somewhere |
| Undeclared id | undeclared-north-star at error severity, naming both the slug and the id |
| Unmigrated project | Empty rollup and neither finding, even with labelled and unlabelled plans in inventory |
The two severities are deliberately different and both follow the locked decision. A plan that points nowhere is a prompt to think, so it is information. A plan that points at something that does not exist is a dangling reference, which is the same fault class as a dangling dependency slug and carries the same error severity — so it is repaired before execution rather than accumulating.
Reporting a zero-work direction matters more than it looks: a direction with no plans pointing at it is exactly the case the project most needs to see, and a rollup that only listed directions with work would silently hide it.