Skip to content

Move the checklist library here from junco, and resolve the two projects into one - #1

Merged
kitadmin merged 15 commits into
mainfrom
claude/move-open-checklists-from-junco
Aug 10, 2026
Merged

Move the checklist library here from junco, and resolve the two projects into one#1
kitadmin merged 15 commits into
mainfrom
claude/move-open-checklists-from-junco

Conversation

@allenmcghan

Copy link
Copy Markdown
Owner

Brings the Open Checklists work out of allenmcghan/junco, where it was commissioned, into the repository the project is served from. Done with git subtree split, so all 13 commits of history come with it rather than landing as one opaque import — git log here now has 15 commits across both lineages.

The companion change is junco#3, which reduces Junco to a reference.

What arrives

The checklist, completion-log, field-report and pilot-logbook schemas; the validators and their negative-case suites; the semantic diff; exporters to eight formats; the public-domain acquisition tooling with pinned hashes; the FAA NASR airport ingest (19,426 airports); the BM25 document library (14 public-domain handbooks); the 14 CFR part 61 certificate requirements; and the static site generator that produces the whole 14-page site including the browser editor.

Start at README.md, then docs/01docs/06 for the legal, format and verification reasoning.

Two projects had grown the same name

This resolves them into one, split by kind rather than by repository:

Format Why
Checklists JSON A document with a source to cite and a verification state that has to survive conversion into every other format
Aircraft profiles TOML Hand-edited hardware configuration. No upstream document, no provenance to record, and someone will edit one in a text editor at a hangar

Forcing either into the other's format would make one of them worse, so the README states the division and the reason.

Three things needed reconciling rather than merging

Repo-wide CC-BY-4.0 → per-file rights. A blanket licence holds while every file is authored here and breaks the moment a transcription of a manufacturer's procedure lands: that is not ours to license out, and a US Government work needs no licence from us at all. The validator already refuses to publish a file whose rights are unresolved or upstream_reserved. The README also no longer links a LICENSE file that does not exist in the repo. The authored profiles and PM-2 checklists remain CC-BY-4.0 and now say so specifically rather than by implication.

The placeholder index.html is removed. A static host pointed at the repository root would serve it instead of the generated landing page — a site that looks fine while serving a stub, which is the kind of failure nobody notices. Nothing it carried is lost: its Nuthatch link and its PM-2 profile and spec links are now on the generated projects page, which had no aircraft-profile section at all before this. The README records that the deploy target is build/site, with the Cloudflare Pages settings spelled out.

CI is re-rooted. No path filter and no working-directory, since the corpus is now the repository rather than a subdirectory of one.

Left deliberately undone

The three PM-2 checklists stay Markdown for now. They carry their "not validated against the PM-2 POH" caveat as prose in a header, which no consumer can act on; converting them to .ocl.json turns that into a structured verification state the site, the exporters and any other software can read. That is the first task after this move — until it happens those files are readable by people but invisible to every tool in tools/.

Verified from this root

6/6 checklists, 27/27 checklist negative cases, 1/1 completion log, 2/2 field reports, 14/14 report cases, 1/1 logbook, 26/26 logbook checks, 20/20 library checks, certificate data emitted and checked against the example logbook, 0 export contract violations, and the site builds to 81 files with Nuthatch and the PM-2 profile present on the projects page.

Note that reviewed: 0 is correct rather than a bug: no checklist here has yet been checked against its source by a second person, and none of the six examples is airworthy — each says so in its own known_issues.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JRrL2B6zmM3jxkTRt2DTVB


Generated by Claude Code

claude added 14 commits August 10, 2026 01:04
Design proposal for Open Checklists, a machine-readable aircraft checklist
corpus. Research and design only — no site, no pipeline, no corpus.

docs/01-legal-research.md
  Copyright doctrine (102(b)/Baker v. Selden, the blank-forms rule, the recipe
  cases as closest analogy, ADA v. Delta Dental as the adverse authority on
  taxonomies), the public-domain lanes, and a survey of existing collections
  and prior open-source attempts.

  Two findings that change the plan. Product liability is the larger exposure
  than copyright: US courts treat aeronautical charts as products for strict
  liability, and Winter v. G.P. Putnam's Sons distinguishes books from charts
  on grounds that put a checklist on the chart side. And a blanket corpus
  licence would assert rights the project does not hold for POH-derived files,
  so rights are per file.

docs/02-format-decision.md
  Recommends canonical JSON, argues the decisive reason is embedded consumers
  rather than tooling, and pushes back on four points: a published JSON Schema
  cannot express the rules that keep the corpus safe, JSON diffs need semantic
  rendering in review, a lossy CSV export must never silently drop a warning,
  and the licence compatibility constraint between efis-editor's Apache-2.0 and
  GPL-2.0.

docs/03-verification-model.md
  Replaces the machine/reviewed/verified ladder with two independent axes.
  Flying behind a checklist cannot detect a dropped item, so operational review
  never lifts a file out of quarantine. Adds evidence requirements, automatic
  demotion on content change, presentation rules, and a takedown process.

docs/04-roadmap.md
  OCR pipeline with source admissibility as a gate separate from transcription
  accuracy, dual-pass transcription for disagreement mapping, and a first
  milestone restricted to public-domain sources with no website.

schema/open-checklist-1.0.schema.json
  JSON Schema 2020-12. Semantic phase vocabulary, challenge/response items,
  non-tickable note/caution/warning types, memory items, and per-file
  provenance, verification and rights blocks.

tools/
  Reference validator running the schema then seven policy checks, with 27
  negative cases covering the safety rules.

examples/
  Four files exercising different rights and verification corners, including a
  Part 103 ultralight and a glider. One is grounded in FAA-H-8083-3C; the rest
  are authored and marked as such. None is airworthy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JRrL2B6zmM3jxkTRt2DTVB
…g question

Follows the clarification that the goal is a phone checklist you tick off during
preflight, a log proving you did it, and printing at arbitrary paper sizes — with
the corpus as fuel for the tool rather than the deliverable.

docs/05-product-and-sourcing.md
  freechecklists.net was reachable this time and was read. It is owned by
  Dauntless Software, a commercial vendor, holds 760 resources, and its DO NOT
  terms state verbatim: "Copy the material from this page and put it on your web
  page. You do not have permission to do this," alongside a prohibition on
  direct-linking files. dauntless-soft.com's robots.txt blocks ia_archiver
  specifically, so mirroring is a considered position rather than an oversight.

  Bulk-scraping therefore fails on three independent grounds: the terms forbid
  it, Dauntless cannot grant permission for contributor-authored material it
  merely hosts, and ingesting 760 files of unresolved provenance would defeat the
  verification model by construction.

  The workable route uses the site as an index — facts about which documents
  exist are not protected — and obtains each document from its real source. Their
  own listing points at the best lane: T-34B NATOPS publications are US Navy
  works, public domain at source. Priority order, permission-request strategy,
  and revised M1-M6 sequencing included.

  Also records the two-edged nature of the log: Part 91 requires no preflight
  record, so once one exists it can be read against the pilot. Design responses
  are no bulk-complete, tick-time timestamps, explicit recording mode, and making
  skips expressible rather than hidden.

schema/open-checklist-log-1.0.schema.json
  Completion log format. checklist.content_hash binds a log to an exact checklist
  version; recording.mode separates live from retrospective; skipped and failed
  states require a reason; aircraft_rejected is a first-class outcome;
  previous_log_hash chains a series so backdating breaks later links.

tools/render.py
  One checklist in, one self-contained HTML out: phone tick-off, selectable paper
  size for print, log export. No external requests, so it works with no signal.
  Information items get no checkbox and there is deliberately no check-all.

tools/validate_log.py
  Schema plus policy: entry references resolved against the checklist,
  information items rejected as completed, timestamps bounded by the session, and
  an implied-working-rate check that declines to treat a physically implausible
  run as evidence of an inspection.

Verified end to end in Chromium at a 390px viewport: 48 tickable items, zero
checkboxes on information items, no horizontal overflow, prints to a 4-page
kneeboard PDF, and the exported log validates. The rate check caught the
automated test run at 0.07s per item, which is what it is for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JRrL2B6zmM3jxkTRt2DTVB
…l transcription

Acts on "pull down as much public domain stuff as we can", and answers the
manufacturer question.

tools/acquire.py + sources/public-domain.json
  Manifest-driven acquisition with SHA-256 recorded per asset and a provenance
  sidecar written per document. A `discover` subcommand searches the Internet
  Archive and prints candidates without downloading anything, keeping the
  admissibility decision human per the Stage 0 gate.

  The host allowlist is the sourcing policy expressed as code: freechecklists.net
  and dauntless-soft.com are in the denylist with the terms quoted as the reason,
  so the refusal survives the memory of the conversation that produced it. Any
  host not on the allowlist is refused rather than silently fetched. Both
  behaviours are exercised.

examples/beechcraft-t-34a-usaf.ocl.json
  First genuinely type-specific transcription, from USAF T.O. 1T-34A-1 (10
  February 1958) — a US Government work with no copyright, for a type civilians
  still fly in the restricted and experimental categories. 117 tickable items, 27
  memory items, across preflight, interior inspection and ten emergency
  procedures. Left at source_fidelity=unreviewed, and sections that fell on
  unreadable pages are omitted rather than reconstructed.

  Measured OCR finding worth keeping: 12 of 164 pages extract as inverted
  mojibake, three of them inside the block marked CUT ON LINE. The cause is the
  original print design rather than the scan — the condensed checklist is a
  cut-out card whose alternating panels are printed upside down so they read
  correctly once folded. So the least reliably OCR'd pages are exactly the ones
  holding the checklist, and /Rotate is 0 on all 164 pages, meaning rotation
  detection has to be per-page and content-based.

docs/05-product-and-sourcing.md section 4
  On manufacturers. The instinct that safety information should reach pilots is
  widely shared and several manufacturers already publish POHs free, but
  obtaining a document lawfully grants no right to republish it: first sale covers
  the copy, not the contents, so scanning solves acquisition and not
  redistribution. Their actual reasons for controlling distribution are revision
  control, product liability and the fact that an approved AFM is part of the type
  certificate — not secrecy.

  Currency is their strongest objection and this format's strongest feature, since
  source.revision, supersedes and content_hash let every derived copy be marked
  stale the day a revision lands. Reframes the ask accordingly.

All five checklist files validate, 27 negative cases still fire, the log
validates, and the schema caught an over-length provenance field in the new file
during authoring.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JRrL2B6zmM3jxkTRt2DTVB
…al premise

Responds to the point that the real differentiator is letting people report a
stale checklist so it can be fixed. That mechanism did not exist in the proposal
and should have.

schema/open-checklist-report-1.0.schema.json, tools/validate_report.py,
tools/test_reports.py, two worked reports in examples/

  A report is structured rather than free text for one reason: a comment thread
  cannot demote a file and a report can. Types cover transcription_error,
  stale_item, source_revision_available, airframe_variation, procedural_concern,
  rights_concern and improvement.

  Three enforced rules give it teeth. A report pins the exact content_hash it was
  filed against, so a report against a superseded version is recognised as history
  rather than a live defect. A confirmed transcription_error or stale_item against
  a file claiming single_reviewed or dual_reviewed errors unless the resolution
  records demoted_verification, because a review of content known to be wrong is
  not evidence. And an open safety_critical report requires a matching
  safety_defect in the file's own known_issues, so the concern travels inside the
  downloaded JSON or printed PDF instead of living only on a website.

  14 cases, including the positive case that the demotion rule accepts the
  situation once the demotion is actually recorded.

  This makes verification a live state that degrades when someone finds a problem
  and recovers when it is fixed, rather than a judgement made once at
  transcription. That is the part a PDF library structurally cannot do.

docs/06-community-currency.md

  Concedes two points. Staleness parity: a transcription made today is exactly as
  current as freechecklists', and the earlier currency section was about why
  manufacturers control distribution, not a comparison against the incumbent.
  And the observed enforcement rate for this activity is approximately zero, which
  is real information about risk even though it is not a legal defence.

  Corrects one premise that points the wrong way: copyright attaches on fixation,
  with no publication, registration, notice or book required. Formal publication
  is where pre-1978 copyrights were lost, not where copyright is gained. The
  stronger argument is available and is the one to stand on: the document is
  copyrighted, but procedure, functional directions and merged expression are not
  protected by that copyright.

  Locates precisely where the flight-club analogy stops. Typing your own checklist
  is not the problem, it is the recommended posture and is what
  rights.status=original_expression means. Personal use is strong, club sharing is
  plausible, and what moves is distribution scale rather than authorship.

  States the remaining disagreement narrowly: transcribing from POHs in your own
  words is supported, covering the same aircraft as freechecklists is fine, and the
  only refused act is bulk-copying another site's files, which rests on their
  stated terms rather than on a copyright judgement.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JRrL2B6zmM3jxkTRt2DTVB
…d CI

The website, wired to the corpus already in the repo. The rights rules are
already encoded in the tooling, so the site inherits them rather than restating
them: acquire.py refuses non-allowlisted hosts, and the validator refuses to
publish a file whose rights are unresolved.

tools/export.py
  Converters to json, csv, tsv, md, txt, xml, docx and html. DOCX is written
  directly as OOXML with no dependency, because Word is where people take a
  checklist to edit it. CSV is one row per item with a type column, deliberately
  not phase-as-column, since that shape cannot represent a warning.

  The safety-preserving export contract from docs/02 is enforced rather than
  intended: verify_export re-reads each generated artifact and fails if a
  warning, caution or memory marker went missing. Proven by feeding it an
  exporter that strips warnings — all seven formats catch it. Two false positives
  found and fixed along the way, both escaping artefacts in the checker rather
  than real drops: JSON and XML mark memory items as fields rather than labels,
  and HTML escapes the apostrophe in "aircraft's".

tools/build_site.py
  Static site, no server, no database, no build-time network access. Filterable
  catalogue, a page per checklist, every download format, a machine catalogue at
  api/index.json with no key or rate limit, stable plain-HTTP paths, a SHA-256
  manifest so a mirror can be verified, separate reviewed and unreviewed bundle
  listings, robots.txt and sitemap.xml.

  Two verification-model rules are enforced here rather than left to templates,
  because a site generator is where they get quietly forgotten: a quarantined
  file carries .UNREVIEWED in its download filenames, and quarantined files are
  kept out of the default bundle listing.

.github/workflows/open-checklists.yml
  Path-filtered to open-checklists/** so it does not run on unrelated Junco work.
  Runs the validators, both negative-test suites, the export contract and the
  site build, and uploads the site as an artifact. This is the point at which the
  corpus rules stop being a document and start being enforced.

Verified in Chromium: 5 cards render, search narrows to 1 on "t-34", the category
and verification filters work, reviewed-only correctly returns 0 of 5, all 35
download links resolve, no console errors. All five files are quarantined, which
is correct — none has been reviewed by a second person.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JRrL2B6zmM3jxkTRt2DTVB
…browser editor

Builds out what the corpus is actually for in the experimental and ultralight
world: engine swaps and panel rebuilds are the norm, so a builder's checklist is
most useful to the next person when they can see what changed and why.

schema: aircraft.airframe_family, aircraft.modifications[], derived_from

  airframe_family groups every variation of one airframe regardless of engine, so
  browsing "what did others flying this do" works. modifications[] records the
  swaps with a kind, an affects_procedures flag and how they were approved
  (part103_none is an honest option, not a gap).

  derived_from records lineage by attribution rather than inheritance. A forked
  file stays complete and standalone, so a pilot always holds the whole checklist
  and never a composition resolved at render time. That was the reason I deferred
  this in 1.0; attribution gives the same discoverability without the ambiguity.
  It stores the parent's hash at fork time, which lets tooling distinguish "the
  parent has since changed" from "this fork diverged".

validate.py: derivation policy

  A variant that names neither a modification nor a changes_summary is rejected —
  a duplicate with no stated reason is corpus noise. An airframe_specific_copy
  must identify its aircraft. A variant must share its parent's family. Warns when
  the parent has moved on and diverged is unset, and when modifications are
  recorded without a family slug, since that file will not surface where people
  look for it.

tools/diff.py

  Semantic diff, not a text diff: matches sections by id and items by id or text,
  then ranks findings so warnings and cautions come first, then memory items, then
  changed control settings. A JSON diff of two checklists buries the line where a
  response went from 2,000 to 2,400 RPM; this leads with it.

site: family pages and the editor

  f/<family>/ lists every variation of an airframe with each fork's diff inline.
  editor.html creates or forks a checklist entirely client-side — no account,
  nothing uploaded. Forking fills in derived_from and computes the parent hash in
  the browser, and information items structurally cannot be given a response or
  made tickable, so the rule that matters most cannot be broken by the editing UI.
  Files it produces are always marked authored, unreviewed and not airworthy;
  those states are earned by someone else checking the file, not claimed by
  writing it.

examples/aerolite-103-kawasaki-340-n512jm.ocl.json

  The worked lineage case: same airframe, Kawasaki 340 twin in place of the Hirth
  single, electric start added. The diff reports 22 procedural differences and the
  changes_summary explains what the swap forced.

Verified in Chromium over HTTP: the family page renders 22 diff findings with the
warnings group first; index search finds files by modification kind and by engine;
the editor forks the baseline, loads 6 sections and 48 tickable items, and its
downloaded file passes the full validator with no errors and no warnings.

Two real defects found by testing rather than assumed away. The browser's SHA-256
of the parent matches the Python content_hash exactly, confirming the canonical
serialization agrees across languages. And the validator caught the editor
emitting memory items without the required id, which is now derived from the item
text and de-duplicated per section.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JRrL2B6zmM3jxkTRt2DTVB
…cy pages

Turns the site into something a builder can actually use: write a checklist, keep
it, fork anyone else's, print it to fit a kneeboard, and install it on a phone.

Saving, and one design call worth stating

  "Save onto the website" has two meanings and they need different answers. Your own
  checklists now persist in browser storage — a My Checklists list with save, open,
  download, delete, import/export of the whole library, and an autosaved draft so a
  closed tab does not cost an hour of typing. Nothing is uploaded, which is what
  makes the privacy policy short and true.

  Writing into the public corpus stays a reviewed path: Contribute downloads the
  file, saves it locally and points at the pull-request steps. Anonymous direct
  writes into a safety library would defeat the verification model entirely, so that
  one is deliberate rather than unfinished.

Forking from anywhere

  editor.html?fork=<id> deep links, a dropdown of the whole catalogue, and a "Fork
  this in the editor" button on every checklist page alongside links to the other
  variations of that airframe.

Printing and phones

  Ten preset paper sizes plus a custom width/height, and a text scale control for
  poor cockpit light: Letter, Legal, A4, A5, A6, kneeboard 5.5x8.5, small kneeboard
  4.25x5.5, index card 5x8, small index card 3x5, half-letter landscape. A web app
  manifest and generated PNG icons make it installable on a phone, and a service
  worker precaches the shell and every checklist page so it opens in a hangar with
  no signal. The worker is network-first so a corrected checklist is never served
  stale from cache.

Branding

  A wordmark and logo — a checkbox whose tick sweeps up like a wing, inline SVG in
  currentColor so it works in both themes — plus a favicon, theme colour, Open
  Graph tags, a landing page at index.html, and the catalogue moved to
  catalogue.html. Tagline: free aircraft checklists that tell you where they came
  from.

Policy pages

  privacy, terms, takedown, contribute and contact. The privacy policy is short
  because there is genuinely almost nothing to disclose: no analytics, no cookies,
  no accounts, no third-party requests, and everything the visitor writes stays on
  their device. Terms lead with the safety notice and set out per-file licensing
  rather than implying one licence covers the corpus. Takedown publishes the
  unpublish-first process, including what the project can offer a manufacturer that
  their own PDF distribution cannot. Contribute carries the contributor warranty
  and says plainly that every submission arrives marked authored, unreviewed and
  not airworthy, because verification is earned by someone else checking it.

Verified over HTTP in Chromium: all eight pages return 200, the landing page and
wordmark render, a checklist saves to the library and survives a reload, the
deep-link fork resolves and computes the parent hash, the fork and variations links
appear on checklist pages, custom paper size produces the expected @page rule, the
text scale changes the computed font size, the service worker registers, and there
are no page errors.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JRrL2B6zmM3jxkTRt2DTVB
…r-month currency

There is no interchange standard for pilot logbooks. ForeFlight, LogTen, Garmin
Pilot, MyFlightbook and Safelog each define their own CSV template, so moving
between them is lossy hand work and a flight history is effectively hostage to a
vendor. Same gap shape as checklists, so the same answer: a documented format the
data can live in and be converted out of.

schema/open-logbook-1.0.schema.json

  Fields follow 14 CFR 61.51 for what must be recorded, plus what 61.56 and 61.57
  need to compute currency. Time buckets are deliberately not mutually exclusive,
  because the regulations are not — the same hour can be PIC and dual received and
  night and cross-country at once.

  Approaches are objects with a type and location rather than a bare count,
  because 61.51(g)(3) requires the location and type of each approach and a number
  alone neither satisfies the rule nor can be audited. Safety pilot name is
  capturable for 91.109. carried_forward keeps imported paper totals separate from
  real entries so currency is never computed from a lump sum. checklist_log_id
  joins a flight to the preflight completion log recorded for it, which is the
  link no other logbook can make.

tools/logbook.py — validate, totals, currency, import, export

  The arithmetic is checked: day + night cannot exceed total, PIC and SIC cannot
  overlap, no component can exceed total, solo and dual on one flight is
  contradictory, full-stop landings cannot exceed landings, and a Hobbs span that
  disagrees with logged time by more than 0.2 is flagged.

  Currency is computed in calendar months, not rolling days. 61.57(c) says "the
  preceding 6 calendar months", which means through the end of that month; a
  rolling-183-day implementation gives the wrong answer near month boundaries and
  gives it in the unsafe direction. Tested at the boundary in both directions,
  plus the fly-to-regain window and the point at which an IPC becomes necessary.

  Import preserves what it cannot map. Unrecognised columns go into
  imported_from.unmapped and are reported, because a silent lossy import is how
  somebody loses a decade of flying. It also skips vendor preamble rows to find
  the real header, parses 1:18 as well as 1.3, and defaults aircraft make and
  category to placeholders while saying so, since currency depends on them.

  Currency output is framed as an aid, not an authority: it cannot see an IPC, a
  type-specific requirement, or whether a landing was truly to a full stop.

Two correctness bugs found by running it rather than reasoning about it. Tailwheel
and tricycle aeroplanes were being merged because they share a category_class, so
tricycle touch-and-goes appeared to satisfy tailwheel currency under 61.57(a)(2) —
now grouped by class and tailwheel separately. And 61.57 was being reported for
Part 103 vehicles, which it does not reach at all, since it governs acting as
pilot in command under Part 61; those now report no requirement with the reason.

26/26 logbook checks pass, and CI runs them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JRrL2B6zmM3jxkTRt2DTVB
19,426 US airports, heliports, seaplane bases, gliderports and ultralight strips
from the FAA's 28 Day NASR Subscription, a public-domain government dataset:
runways with surfaces and condition, 37,537 radio frequencies with callsigns and
tower hours, fuel, pattern altitude, field elevation, magnetic variation and
sectional. Includes the 14,259 private strips most apps leave out, which matter for
exactly this project's audience.

tools/airports.py

  Reads the inner CSV_Data bundle rather than the 232 MB fixed-width APT.txt.
  Emits a lean search index plus 35 detail shards keyed on the first character of
  the identifier, because 19,426 individual pages would make the build slow and the
  deploy heavy. Frequency use codes are translated to labels a pilot reads, with
  unknown codes passed through rather than dropped.

  Two things found by testing: NASR keys on the FAA identifier, so KFDK is stored
  as FDK and lives in a different shard than its ICAO id implies — there is now a
  shipped ICAO-to-ident map rather than leaving every consumer to rediscover that.
  And remarks were a third of the payload, so they are capped at eight per airport.

  The ingested output is ~30 MB, so data/ is gitignored and the site build ships
  airport pages only when it is present. NASR is in the acquisition manifest with a
  note to re-ingest every 28 days.

Staleness is handled rather than ignored: the AIRAC effective date is carried
through into cycle.json, and the page shows how old the data is and says
SUPERSEDED once a newer cycle has been published. A stale frequency is worse than
no frequency.

Weather, and why there is a Worker

  Live weather cannot be done from a static page: aviationweather.gov serves its
  API to a server but sends no Access-Control-Allow-Origin header, which I
  confirmed in a browser. Rather than abandon the static architecture, the site
  ships worker/weather-proxy.js — a one-file Cloudflare Worker, which adds no new
  infrastructure given the site is going behind Cloudflare anyway. It restricts
  itself to that one upstream host and a fixed set of products, caches for 60
  seconds because weather cached longer lies, and logs no identifiers so the
  privacy promise stays true. Until it is deployed the page says exactly what is
  missing and links to the official source instead of silently showing nothing.

  Weather is deliberately excluded from the service worker precache. Everything
  else here degrades gracefully when stale; a METAR does not.

Verified in Chromium: the cycle notice reports the data as 4 days old, searching
KFDK resolves to FDK and opens Frederick Muni with 16 frequencies including tower
hours, both runways render, the deep link ?a=W29 opens a private strip, and there
are no page errors.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JRrL2B6zmM3jxkTRt2DTVB
Completes the feature set: a searchable public-domain document library with cited
results, a registry for the documents that cannot be hosted, a projects page, and
an honest answer on charts.

tools/library.py + search.html

  Full-text index over public-domain maintenance documents, currently AC 43.13-1B
  (Acceptable Methods, Techniques and Practices) and the AMT General Handbook —
  1,342 pages, 5,164 passages, 14,807 terms. BM25 scoring, an inverted index
  sharded by first letter, and passages sharded in buckets of 500 so a query
  fetches a few hundred KB rather than the 4 MB store. The browser implements the
  same tokeniser and scoring as the CLI, so results match: "magneto timing" returns
  AC 43.13-1B paragraph 8-20 on page 397 in both.

  It cites, it does not diagnose. Every result is a passage with document, revision
  and page, and the page says so above the search box rather than in a footnote. A
  generated answer would be the only thing on this site with no provenance, and
  "your problem is probably X" is the claim that hurts somebody when it is wrong.
  Retrieval is also more useful, because it hands over the actual text. If an LLM
  is added later this is the correct substrate: retrieve first, summarise these
  passages with these citations, never answer from memory.

  Ingest enforces the same admissibility gate as the checklist corpus, reading the
  provenance sidecar acquire.py writes and refusing anything not public_domain —
  so a manufacturer manual cannot be indexed by accident.

  Manufacturer and engine manuals are instead a registry of ten entries: what the
  document is, its part number where known, whether it is free or purchased, and
  where to get it. Listing what exists is a fact about the world, not a copy.
  Rotax, Cirrus, Hartzell and Jabiru publish freely; Lycoming, Continental, Textron
  and Piper sell theirs, and the page says which is which.

charts.html

  Explains why the project links to FAA charts rather than re-cutting them, and the
  reason is liability rather than copyright: FAA charts are government works you may
  freely copy, but courts have treated aeronautical charts as products subject to
  strict liability, and a customised chart a pilot navigates from is the exact fact
  pattern. Every other artifact here can carry a verification state; a chart you fly
  by cannot be made safe with a label. Links to all five FAA digital product lines
  and to SkyVector, and points out that the airport pages already carry the field
  data you would otherwise read off a chart.

projects.html

  Open Checklists, Junco and the flight simulator, framed around the thing that
  actually connects them: the formats. A checklist only this website could read
  would be worth little, which is why the schemas are permissive enough for closed
  products to adopt.

Verified in Chromium: all seven pages return 200, the search returns 12 ranked hits
with correct citations and term highlighting, the deep link ?q= works, the registry
renders ten entries, and there are no page errors. Full suite still green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JRrL2B6zmM3jxkTRt2DTVB
…from a logbook

Serves student through commercial, and the second half is the part nothing free
does well.

tools/training.py + training.html — progress toward a certificate

  Encodes the aeronautical experience requirements from 14 CFR part 61 for sport,
  private, instrument, commercial, CFI and ATP, then reads a real logbook against
  them. Runs client-side on the site, so a logbook never leaves the device, and the
  browser implements the same arithmetic as the CLI — verified identical on the
  example logbook: 4 met, 6 short, 1 undetermined, 3 to confirm.

  Three rules keep it honest, because a student will act on it.

  Every line cites its CFR paragraph, not just an hour count. If the tool and the
  regulation disagree the regulation wins, and the citation is how you find out.

  What a logbook cannot answer is left unchecked rather than guessed. Whether an
  airport had an operating control tower is not a logged field, so 61.109(a)(5)(iii)
  reports "cannot be determined from a logbook" instead of failing someone who has
  met it. Instructor endorsements and knowledge tests are listed as confirm-manually
  and never counted toward a total that would look complete. Where a logbook records
  night cross-country flights but no distances, that reads as undetermined rather
  than short.

  Intersected buckets understate rather than overstate. A logbook records
  cross-country and PIC hours but not their overlap, so the tool takes the smaller
  of the two per entry. It also discloses when carried-forward totals had to be
  excluded from an intersected requirement — 78.9 hours in the example — rather than
  silently dropping them, because a student would otherwise think the tool had lost
  their hours.

  It reports; it does not certify.

Study library

  Sixteen entries covering what the FAA publishes free: PHAK, the Airplane,
  Instrument, Instrument Procedures, Weather, Risk Management, Weight and Balance
  and Instructor handbooks, the handbooks for glider, helicopter, balloon, powered
  parachute and weight-shift control, the ACS documents, the knowledge-test figure
  supplements, the Chart User's Guide, advisory circulars, FAA Safety Briefing and
  WINGS. Plus a suggested reading order that puts the ACS second rather than last,
  since it tells you what you will actually be tested on.

Library now indexes 11,469 passages across 12 documents

  Added PHAK (522 pages), the Instrument Flying Handbook (370), the Weight and
  Balance Handbook (112) and five ACS documents, on top of AC 43.13-1B and the AMT
  General Handbook. All US Government works, so full text is publishable. PHAK
  needed a retry — the first fetch truncated at 67 of 77 MB, which the manifest
  hash then caught.

Verified in Chromium: six certificate tabs, sixteen study cards, a saved logbook
loads from browser storage, progress bars and per-requirement notes render, tab
switching recomputes, and the carried-forward disclosure appears. CI now emits the
requirement data and checks progress on every run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JRrL2B6zmM3jxkTRt2DTVB
Ingests the two documents I flagged as the highest-value gaps in the library:
the Aviation Weather Handbook (FAA-H-8083-28B, not the 28 the FAA landing page
is named for) and the Risk Management Handbook (FAA-H-8083-2A). Both are US
Government works. The library goes from 12 documents to 14 and from 11,469
passages to 13,518.

Indexing them exposed a real bug. The T-34A directory holds two renderings of
one handbook -- the scan and an OCR text layer -- and ingest looped over paths,
so that document was indexed twice, the second pass overwrote the first's
metadata, and 402 passages sat in the store attributed to a document whose
recorded count did not include them. Per-document counts no longer summed to the
total. Ingest now groups assets by directory, deduplicates identical passages
within a document, and reports pages as the longest rendering rather than the
sum of both.

That grouping also makes each document's passage ids contiguous, which buys a
per-document search filter for free: limiting a search to one handbook is a range
test on the passage id, with no extra fetches and no change to the shard layout.
The study cards on the training page link straight into it, so "Aviation Weather
Handbook" now goes to a search scoped to that handbook. An unknown or stale doc
parameter falls back to searching everything rather than returning nothing, and a
scoped search that finds nothing offers to widen instead of dead-ending.

tools/test_library.py asserts the parts that a correct-looking search result
cannot show you: that the admissibility gate rejects rather than flags, that one
directory is one document, and that passage id ranges partition the store with no
gaps or overlaps. A gap there would print one document's text under another
document's citation, which is worse than no result. Wired into CI.

acquire.py gains --pin, and every asset in the manifest is now pinned. Without a
recorded hash a later fetch cannot distinguish a corrected document from one
silently replaced at the same URL, and agencies do reissue PDFs in place. The
provenance sidecar records the digests too, since it travels with the bytes and
is what the index reads. nfdc.faa.gov joins the host allowlist, with a note --
it publishes the NASR data the airport pages already ship.

Verified in Chromium against the built site: the deep link, the widen fallback,
the unknown-parameter fallback, and the training-card links, with no console
errors. Python and browser agree on match counts for the same query.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JRrL2B6zmM3jxkTRt2DTVB
Moving this to another machine surfaced a gap: pymupdf is required by the library
ingest and was documented nowhere except the SystemExit message that fires when
it is missing. The 640 MB of downloaded sources, generated indexes and built site
are all gitignored, so a clone needs the pipeline spelled out rather than
scattered across the tool reference.

Records two things a new machine needs to know. The site builds with no downloads
at all -- checklists, editor, exporters and policy pages come from examples/, and
the airport, weather and search pages explain that their data is missing rather
than breaking; verified from a fresh clone, 14 pages and the whole test suite
green with nothing fetched. And the NASR manifest entry names a specific 28-day
cycle, so once that cycle passes the fetch 404s and the URL needs updating, which
is not obvious from a bare "not found".

Also states what hash pinning is for at the point where someone will hit it: a
MISMATCH means the document changed upstream, and re-pinning is a deliberate
decision after checking what changed, not a way to clear the error.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JRrL2B6zmM3jxkTRt2DTVB
Brings the Open Checklists work out of allenmcghan/junco, where it was
commissioned, into the repository the project is actually served from. Done with
git subtree split, so all 13 commits of history come with it rather than landing
as one opaque import.

What arrives: the checklist, completion-log, field-report and pilot-logbook
schemas; the validators and their negative-case suites; the semantic diff;
exporters to eight formats; the public-domain acquisition tooling with pinned
hashes; the FAA NASR airport ingest; the BM25 document library; the part 61
certificate requirements; and the static site generator that produces the whole
14-page site including the browser editor.

Two projects had grown the same name, so this resolves them into one with a split
by kind rather than by repository. Checklists stay JSON: they are documents with a
source to cite and a verification state that has to survive conversion to every
other format. Aircraft profiles stay TOML: they are hand-edited hardware
configuration with no upstream document and no provenance to record, and someone
will edit one in a text editor at a hangar. Forcing either into the other's format
would make one of them worse, so the README now states the division and why.

Three things had to be reconciled rather than merged:

The repo-wide CC-BY-4.0 claim is replaced with per-file rights. It holds while
every file is authored here, and breaks the moment a transcription of a
manufacturer's procedure lands -- that is not ours to license out, and a US
Government work needs no licence from us at all. The validator already refuses to
publish a file whose rights are unresolved or upstream_reserved. The README also
no longer links a LICENSE file that does not exist; the authored profiles and PM-2
checklists are still CC-BY-4.0 and now say so specifically.

The placeholder index.html is removed, because a static host pointed at the
repository root would serve it instead of the generated landing page -- a site
that looks fine while serving a stub. Nothing it carried is lost: its Nuthatch
link and its PM-2 profile and spec links are now on the generated projects page,
which had no aircraft-profile section at all before this, and the README records
that the deploy target is build/site with the Cloudflare Pages settings spelled
out.

The CI workflow is re-rooted: no path filter and no working-directory, since the
corpus is now the repository rather than a subdirectory of one.

The PM-2 checklists are deliberately left as Markdown for now. They carry their
"not validated against the PM-2 POH" caveat as prose in a header, which no
consumer can act on; converting them to .ocl.json turns that into a structured
verification state. That is the first task after this move, and until it happens
those three files are readable by people but invisible to every tool in tools/.

Verified from this root: 6/6 checklists, 27/27 negative cases, 1/1 log, 2/2
reports, 14/14 report cases, 1/1 logbook, 26/26 logbook checks, 20/20 library
checks, certificate data emitted, 0 export contract violations, and the site
builds to 81 files with Nuthatch and the PM-2 profile present on the projects
page.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JRrL2B6zmM3jxkTRt2DTVB
allenmcghan pushed a commit to allenmcghan/junco that referenced this pull request Aug 10, 2026
The aircraft profile spec already redirected to the openchecklists repository.
The checklist library has now moved there as well (allenmcghan/openchecklists#1),
so the reference should cover it: the checklist, preflight-log and pilot-logbook
schemas are all defined there and consumed here.

Records the split by kind, because a reader who sees TOML profiles and JSON
checklists in one project will otherwise assume one of them is a mistake.
Profiles are hand-edited hardware configuration with no upstream document to
cite; a checklist carries a source and a verification state that has to survive
conversion into every other format. Different problems, different formats.

Also names the property Junco actually depends on rather than leaving it
implicit: checklist phase identifiers are stable and semantic, which is what
lets the app map a section to a moment in flight instead of to a page number.

No functional change to firmware, app, BLE spec, or log format.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JRrL2B6zmM3jxkTRt2DTVB
Rework the central CSS tokens/components in build_site.py (sticky pill nav,
tinted hero, elevated cards, pill badges, modern inputs; remove dark-mode
auto-switch so the background is always white) and align the standalone
checklist renderer in render.py (larger tap targets, refreshed toolbar,
soft-tinted safety banners). Mobile-first throughout; sub-pages inherit the
shared CSS variables.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kitadmin
kitadmin merged commit e4f65ff into main Aug 10, 2026
allenmcghan pushed a commit that referenced this pull request Aug 17, 2026
…name)

The council's #1 next feature. Closes the plan→fly→log loop.

- D1: logbook_entries table (date, dep/arr, route, aircraft, total/PIC time,
  landings, remarks, source, source_ref) with a unique (user_id, source_ref)
  index for import de-duplication.
- Worker: GET /api/me/logbook (entries + running totals), POST /api/me/logbook
  (add/edit), POST /api/me/logbook/import (pulls saved plans → entries, deduped),
  DELETE /api/me/logbook/:id. Auth-gated like the rest of /me/*.
- Profile "My logbook": running totals (flights, total hrs, PIC, landings),
  one-tap "Import from my saved plans", add-a-flight form, delete, and a clear
  61.51 "this is a convenience record, not your legal logbook" note.
- Renamed "Recent preflights" → "Recent checklist completions" with a note that
  they are NOT the flight logbook (per council: avoid 61.51 confusion).
- SkyWatch per-flight insurance leadgen: an honest external CTA (we don't sell
  or underwrite; unverified pre-check data is not a basis for coverage) — keeps
  us out of the underwriting/liability chain the council warned about. Drop the
  real leadgen/affiliate URL into #ins-link.

Verified: routes deploy + auth-gate (401); logbook SQL validated on live D1
(import dedup, totals, ordering); profile JS syntax-clean, section deployed.
Signed-in click-through not completed (Zitadel reset email did not reach the
test mailbox); logic mirrors the proven /me/aircraft + /me/airports handlers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

3 participants