Skip to content

Flatten the consumer-keyed tree into the published layout#10

Merged
mmcky merged 1 commit into
mainfrom
data/restructure-flat-tree
Jul 16, 2026
Merged

Flatten the consumer-keyed tree into the published layout#10
mmcky merged 1 commit into
mainfrom
data/restructure-flat-tree

Conversation

@mmcky

@mmcky mmcky commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

PLAN Phase 2. Moves the data out of lecture-python-intro/{static,dynamic}/ into lectures/ — the flat published root — and lifts scripts/ to the repo root, outside the published tree.

Why flat, and why now

The layout is flat because the URL is the interface: lectures/<filename> maps to data.quantecon.org/lectures/<filename>. A consumer-keyed folder implies a series owns a file, which is false — any lecture may consume any dataset — and would bake an owner into every URL.

Doing it now is the whole point. Zero lectures reference this repo, so every move is free. I verified that independently rather than relying on the audit — an org-wide code search for QuantEcon/data/raw, raw.githubusercontent.com/QuantEcon/data and the old static paths returns 0 hits; the only references to data-lectures anywhere are this repo's own docs. Once the first repoint merges (Phase 8), published filenames become an API and every move is a breaking change. AGENTS.md now says exactly that, where it previously promised the freedom.

Layout decisions (PLAN Phase 2's open question)

Path Published
lectures/ — flat, all 9 datasets yes
scripts/ — builders no
manifest-schema.yml — the schema sketch no

Manifests are the one exception: they live inside lectures/ as sidecars named <filename>.yml, so a dataset can't be moved or removed without its metadata and CI can assert the pairing with a glob. Naming uses the full filename (mpd2020.xlsx.yml, not mpd2020.yml) because a stem-keyed sidecar collides the moment one dataset ships in two formats — precisely the fig_3.xlsx/fig_3.ods case this repo already contained. All strawman until the pilot tests it.

Three files dropped rather than promoted

Rather than carry files with no consumer into the canonical namespace (each would then need a manifest, licence check and integrity verification under Phases 6–7):

  • GDP_per_capita_world_bank.csv and Metadata_Country_API_NY.GDP.PCAP.CD_DS2_en_csv_v2_4770417.csv — an org-wide code search returns zero references to either, they are freely re-downloadable from the World Bank, and their licence was never established.
  • fig_3.ods — verified to carry no provenance the published .xlsx lacks: both parse to a single Sheet1, identical 34×6 shape, DataFrame.equalsTrue. A pure format twin.

All three remain in git history if ever wanted. Happy to keep any of them if you'd rather — this is the Phase 6 keep-or-drop call made explicit.

Reviewing this — read the layout, not the schema

Two things worth knowing before you spend time here.

manifest-schema.yml is a strawman, and #12 already revises it by ~46 lines. It is labelled provisional in its own header ("every field here is provisional until proven by a real migration") and that is exactly what happened: the pilot in #12 met one real dataset and the schema needed integrity split into migration/upstream, a builder_status field, and known_nulls. So feedback on the schema's fields is better spent on #12, which carries the tested version. What is worth reviewing here is the layout decision — flat tree, sidecar manifests, full-filename naming — because that is what everything downstream inherits.

There is no CI in this repo (no .github/ — that is PLAN Phase 5). "MERGEABLE" here means "no conflicts", not "verified". The verification is the manual work described below.

Verification

  • All 11 data files are tracked as renames (R in git status -M), so history follows them.
  • business_cycle.py wrote to ../dynamic, which no longer exists — repointed at lectures/ and confirmed its path logic resolves to the real directory for all three of its outputs. A restructure that moved the data but left the builder writing into the void would have failed silently at the next refresh.

Also here

manifest-schema.yml sketches the per-dataset manifest. It is worked against a real example rather than a hypothetical one: business_cycle_metadata.md already records the World Bank series as CC BY-4.0 with source and periodicity, which proves those fields are actually obtainable and gives Phase 6 its model.

Part of #8
See QuantEcon/meta#336
Part of QuantEcon/workspace-lectures#14

Moves the data out of lecture-python-intro/{static,dynamic}/ into lectures/,
the flat published root, and lifts scripts/ to the repo root, outside the
published tree. This is PLAN Phase 2.

The layout is flat because the URL is the interface: lectures/<filename> maps
to data.quantecon.org/lectures/<filename>. A consumer-keyed folder would imply
a series owns a file, which is false — any lecture may consume any dataset —
and would bake an owner into every URL.

Doing this now is deliberate. Zero lectures reference this repo (verified by
org-wide code search, not just the audit), so every move here is free. Once the
first repoint merges, published filenames become an API and each move is a
breaking change. AGENTS.md now says so where the old note promised the freedom.

Three files with no consumer anywhere were dropped rather than promoted into
the canonical namespace: the two World Bank CSVs (zero org-wide references,
re-downloadable at will, licence never established) and fig_3.ods (confirmed a
pure format twin of fig_3.xlsx — identical sheet, identical 34x6 frame). All
remain in git history.

Also sketches the per-dataset manifest schema (manifest-schema.yml) and
repoints business_cycle.py at lectures/, which would otherwise write to a
directory that no longer exists.

Part of #8
See QuantEcon/meta#336
Part of QuantEcon/workspace-lectures#14

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 15, 2026 23:21

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements PLAN Phase 2 by flattening the legacy consumer-keyed layout under lecture-python-intro/ into a flat published tree at lectures/, while moving builder scripts to a non-published scripts/ directory at the repo root. It also documents the new layout and introduces a strawman per-dataset manifest schema to support upcoming metadata/integrity phases.

Changes:

  • Flatten/move datasets into lectures/ and remove the legacy lecture-python-intro/{static,dynamic,scripts}/ READMEs and orphaned World Bank metadata CSV.
  • Repoint the business_cycle.py builder to write outputs into lectures/ and document builders under scripts/.
  • Update repo docs (README.md, PLAN.md, AGENTS.md) and add manifest-schema.yml to describe/standardize the new published layout and manifest approach.

Reviewed changes

Copilot reviewed 10 out of 23 changed files in this pull request and generated no comments.

Show a summary per file
File Description
scripts/README.md New documentation for dataset builders living outside the published tree.
scripts/business_cycle.py Updates output directory from legacy dynamic path to lectures/.
README.md Documents the new flat layout and clarifies what is published vs not.
PLAN.md Records Phase 2 completion details and updates roadmap language accordingly.
manifest-schema.yml Adds a strawman manifest schema and sidecar placement convention.
lectures/nom_balances.npy Moves dataset into the flat published lectures/ namespace.
lectures/fig_3.xlsx Moves dataset into the flat published lectures/ namespace.
lectures/chapter_3.xlsx Moves dataset into the flat published lectures/ namespace.
lectures/caron.npy Moves dataset into the flat published lectures/ namespace.
lectures/business_cycle_metadata.md Publishes World Bank metadata dump alongside the dataset for provenance/license capture.
lectures/business_cycle_info.md Publishes series info dump alongside the dataset.
lectures/business_cycle_data.csv Publishes the dynamic snapshot CSV under the new published root.
lecture-python-intro/static/README.md Removes legacy consumer-keyed static README (layout is no longer consumer-keyed).
lecture-python-intro/static/Metadata_Country_API_NY.GDP.PCAP.CD_DS2_en_csv_v2_4770417.csv Drops an orphan/no-consumer file rather than promoting it into the published namespace.
lecture-python-intro/scripts/README.md Removes legacy consumer-keyed scripts README (scripts moved to repo root).
lecture-python-intro/dynamic/README.md Removes legacy consumer-keyed dynamic README (dynamic output now lives in lectures/).
AGENTS.md Updates repo map and documents the “published filenames are an API” constraint post-repoint.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@mmcky
mmcky merged commit 52dbb89 into main Jul 16, 2026
1 check passed
@mmcky
mmcky deleted the data/restructure-flat-tree branch July 16, 2026 22:45
mmcky added a commit that referenced this pull request Jul 16, 2026
Two small doc corrections surfaced reviewing the pilot:

- AGENTS.md's manifest field list was written for the #10 schema sketch and
  never gained the fields P1 added: license.verified, the integrity
  migration/upstream split, schema.known_nulls, and builder_status. Bring it
  current and point to manifest-schema.yml as the authoritative field reference
  so the two stop drifting.
- manifest-schema.yml: note that row_count_floor is the exact row count for a
  frozen historical extract by design (P1 lingcod = 82), so it is not later
  "relaxed" as if it were a mistake; a loose floor is for files that grow.

See QuantEcon/meta#338

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mmcky added a commit that referenced this pull request Jul 16, 2026
* P1 pilot: add lingcod_msy_recovery.csv with its manifest

The first dataset to land in the published tree, and the first manifest
written against the schema sketch. Consumed by lecture-python-intro's
msy_fishery lecture, which currently reads it from a relative path — so the
downloaded notebook cannot resolve it. The repoint is the paired lecture PR.

Licence established before adding, per AGENTS.md: RAM Legacy is CC BY 4.0,
attribution-only, so rehosting this extract is permitted. Worth recording how
that was found — ramlegacy.org states no licence at all; the authority is the
Zenodo record behind its DOI (license id "cc-by-4.0", access_right "open").

Meeting a real file changed the schema in three ways, all folded back into
manifest-schema.yml:

- integrity splits into `migration` and `upstream`. Lingcod is a byte-perfect
  copy of what the lecture consumes, yet its upstream provenance cannot be
  re-derived at all. One "verified" field would have to overstate or
  understate it; two fields tell the truth.
- `builder_status` records `unrecovered`. This file is constructed but ships
  no builder — the bug AGENTS.md names. The tempting fix is to relabel it
  verbatim, which buries the gap; instead it is now visible in the catalog.
- `known_nulls` declares nulls that are correct. F/Fmsy is null in the
  terminal assessment year, which a blanket no-nulls validation rule would
  wrongly reject.

Part of QuantEcon/meta#338
See QuantEcon/meta#336

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Codify the two inherited-file manifest states (P1 decisions)

The P1 lingcod manifest declares `retrieved: null` and `class: constructed`
with `builder: null` / `builder_status: unrecovered`. Copilot flagged both as
out of compliance with AGENTS.md — correctly, because the rules had no wording
for inherited files. Rather than patch the manifest to dodge the rules (which
would mean inventing a retrieval date or misclassifying a constructed file as
verbatim), bless both states explicitly so the gap stays visible in the
generated catalog and the rules and the manifest stop contradicting.

- AGENTS.md: add "Two inherited-file states" under the manifest rules —
  `retrieved: null` permitted for inherited-undated bytes (paired with an
  integrity.upstream reason; never reconstructed from git history), and
  `builder_status: unrecovered` for inherited constructed files (Phase 9),
  inherited-only so a new constructed file still must ship its builder.
- manifest-schema.yml: document the `retrieved` null policy and scope
  `unrecovered` to inherited files at the two field sites.

Provisional P1 decisions, to be folded into the convention.
See QuantEcon/meta#338 and QuantEcon/QuantEcon.manual#108.

* Tidy manifest docs after the P1 pilot

Two small doc corrections surfaced reviewing the pilot:

- AGENTS.md's manifest field list was written for the #10 schema sketch and
  never gained the fields P1 added: license.verified, the integrity
  migration/upstream split, schema.known_nulls, and builder_status. Bring it
  current and point to manifest-schema.yml as the authoritative field reference
  so the two stop drifting.
- manifest-schema.yml: note that row_count_floor is the exact row count for a
  frozen historical extract by design (P1 lingcod = 82), so it is not later
  "relaxed" as if it were a mistake; a loose floor is for files that grow.

See QuantEcon/meta#338

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Reframe licensing as cache-not-host, and metadata as capture-when-available

Two tone corrections to the manifest convention, from review discussion — the
fields are unchanged, only the surrounding policy language:

- Reframe the repo's identity as a stability CACHE for lecture data (snapshots
  upstream sources so a build doesn't depend on a live API or third-party host),
  not a content-distribution host. The licence question follows: "is this source
  OK to cache and serve publicly, with attribution?", not "may we republish it as
  our own?". Attribution (the manifest `source` block) is the primary obligation;
  redistribution becomes a cheap binary gate (permitted | restricted) that is a
  fast yes for public data sources and blocks only genuinely restricted ones
  (e.g. FRED third-party series). Softens "promoted public host" / "blocker".
- State the capture-when-available principle explicitly: rich provenance is
  always welcome and worth recording when the source provides it, but a missing
  field is recorded as a reasoned gap, never a bar to landing a useful dataset.
  Effort scales with the source — a clean one gets a short manifest.

Does not touch the integrity method/upstream field structure — that simplification
is still under discussion.

See QuantEcon/meta#338 and QuantEcon/QuantEcon.manual#108

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Simplify integrity to one axis: upstream + sha256, note not method

Applies the simplification agreed in review discussion. The P1 finding stands —
migration and upstream are independent questions with different answers — but
only one of them belongs in the manifest:

- Drop the `migration` sub-block. Migration safety is a ONE-TIME check that
  matters at the repoint and never again, so it lives in the repoint PR as a
  Phase 8 gate rather than as a permanent manifest field. The manifest keeps
  `sha256`, which makes the check reproducible at any later date. Lingcod's
  verified migration (byte-compare against intro's copy, 2026-07-16) is
  preserved as a comment pointing at data-lectures#12 / lecture-python-intro#792.
- Replace free-text `method` with an optional one-line `note`, populated only
  when status is not a clean verified — the valuable prose was always the
  REASON (why lingcod is unverifiable), not a re-typed procedure.
- Move the generic how-to-verify-by-class recipe to AGENTS.md, stated once,
  instead of being repeated in every manifest.
- Add `against`: what a verified check compared to (URL, DOI, or repo path),
  so "verified" is auditable rather than a bare assertion.

Net effect: a clean verbatim file now carries a three-line integrity block with
zero prose; only genuinely messy provenance earns a note.

See QuantEcon/meta#338 and QuantEcon/QuantEcon.manual#108

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Bring PLAN.md in step with the P1 schema decisions

AGENTS.md's own rule: when a decision settles, update PLAN.md in the same PR
that acts on it. This PR revised the manifest schema in ways four PLAN
passages still described in their pre-pilot form:

- "Where we're going": the manifest field list predated the pilot — point to
  manifest-schema.yml as authoritative and name the fields P1 added
  (integrity, builder_status, known_nulls, license.verified).
- Phase 5: link both the invariant-tests bullet and the business_cycle.py
  retrofit to #14, which carries the builder template and the schema decisions
  the tests will force (column patterns, known_nulls semantics, dtype
  vocabulary).
- Phase 6: the licence check is per SOURCE, not per file — a cheap binary
  cache-and-serve-with-attribution gate, per the cache-not-host reframe. RAM
  Legacy (P1) joins World Bank as the second source already established.
- Phase 7: the byte-compare is recorded in the repoint PR as a one-time gate
  (not a manifest field); verification results land in integrity.upstream as
  sha256/status/against/date, replacing the retired "method" field; and
  unverifiable-with-note is the honest known status.
- Open-decisions table: licensing strawman restated in cache-not-host terms.

See QuantEcon/meta#338 and QuantEcon/QuantEcon.manual#108

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
mmcky added a commit to QuantEcon/lecture-python-intro that referenced this pull request Jul 17, 2026
Repoints msy_fishery at the canonical copy in QuantEcon/data-lectures and
drops the local one, which nothing else reads.

This also fixes the lecture rather than merely relocating its data. The read
was a relative path, 'datasets/lingcod_msy_recovery.csv', which only resolves
inside a repo checkout — a downloaded notebook has no datasets/ directory, so
the cell raised FileNotFoundError in Colab. Reading over https is what makes
the notebook self-contained.

Uses the interim URL form github.com/QuantEcon/data-lectures/raw/main/… and
the data_url convention already used by long_run_growth and inflation_history.

Blocked until the data is on data-lectures main (QuantEcon/data-lectures#10,
then #11) — until then this URL 404s. The branch URL resolves today, but
pointing at a non-default ref is exactly the bug meta#337 is cleaning up in
mle.md, so it is not a shortcut worth taking.

Verified by running the cell against the data over https: 82 rows, figure
renders, terminal-year null in F/Fmsy handled. Output is unchanged — the
migrated file is byte-identical (sha256 fc2f873f…) to the copy removed here.

Part of QuantEcon/meta#338
See QuantEcon/meta#336

Co-authored-by: Claude Fable 5 <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.

2 participants