Commit graph

5 commits

Author SHA1 Message Date
1747238a83 fix(materialize): scope §5 replacement to the running manifest's stem
The §3 ownership scan classified every ingest-stamped file as replaceable,
so materializing one manifest into a bundle deleted the ingest files another
manifest had stamped there — and removed their index links too.

Narrow ownership to files whose stamp names the running manifest by stem.
The stamp is `{stem}@{sha256[:16]}`; matching on the stem, not the whole
stamp, lets an edited manifest still reclaim the files a prior run of the
same manifest wrote (the stem is stable across content edits), while a
different manifest sharing the bundle keeps its own files. Implements the
trinn-e §10.2 decision. This does not close the operator-copy restriction
(a generated file copied into curated content), which stays documented in
ingest-spec.md:69-72, not enforced.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBbjgS5A55RVavoyjJC4FX
2026-07-23 07:00:27 +02:00
c011534e9f fix(materialize): emit frontmatter and index values verbatim
a7e1bc8 unified title rendering at both sites and routed the open question
to commons, the spec owner: does §5's whitespace collapse apply to `title`,
or only to `source_query`? The ruling is source_query only. §5 mandates the
collapse for that one field — where a multi-line SQL SELECT must render on
one line — while every other value is validated single-line at manifest load
and emitted as-is: validation, not repair.

_single_line is renamed _collapse_whitespace and now runs for source_query
alone. Frontmatter values, the index link label, and the labels_by_target
map all pass through verbatim. A title carrying an internal whitespace run
(or any other operator-supplied bytes) now survives byte-for-byte at both
the frontmatter and the index-label site, instead of being silently altered.

No golden bytes change: no title in the shipped fixtures or either consumer
carries a collapsible whitespace run.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBbjgS5A55RVavoyjJC4FX
2026-07-23 06:41:27 +02:00
a7e1bc88d8 fix(materialize): render a title identically in frontmatter and index
ingest-spec §4 makes the extraction title BOTH the `title` frontmatter and
the index link label — one value, two sites. The frontmatter renderer
single-lined its values while the index label was written raw, so a title
carrying a whitespace run rendered two different ways from one input:

    title: Energiforbruk 2024
    - [Energiforbruk  2024](ingest-orders.md)

Single-lining is now one named rule applied at every site that renders the
title, instead of a detail of frontmatter rendering that the index path did
not share.

No golden bytes change: no title in this repo, portfolio-optimiser or
portfolio-optimiser-claude carries a whitespace run.

Open question routed to commons, the spec owner: §5 mandates collapsing
whitespace runs in `source_query` only, while `title` is separately required
to be single-line and is already validated as such at manifest load. So
collapsing the title may be over-application — the alternative reading is
that both sites should emit it verbatim. Either reading keeps the two sites
in agreement, which is what this commit establishes; which of the two is
correct is a spec question, not ours.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WdVgowYC4LARgvNdNMiuvz
2026-07-20 10:35:57 +02:00
d3a3bccfbb feat(api): expose the §5 provenance stamp on IngestResult
The stamp ({stem}@{sha256(raw)[:16]}) was computed internally but only
written into frontmatter, forcing consumers to recompute it from the
manifest bytes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 03:46:52 +02:00
0ff946696c feat(materialize): add §5 materialization with collision gate and replacement
TDD step 5: materialize_bundle takes three explicit inputs (manifest,
bundle dir, regex-validated ingested_at — no wall-clock default) and
stages every extraction in memory before the first disk mutation. The
§3 collision gate refuses to overwrite any file without the ingest
stamp, before any mutation; replacement removes exactly the stamped
set. Frontmatter is the seven §5 keys in spec order with whitespace
collapse; output is LF-only raw bytes with one trailing newline; the
provenance stamp hashes the same bytes that are parsed. Fresh index.md
gets bundle_summary plus idempotent links in extraction order (§6
creation path; preserve/removal rules come next). The §8 network gate
refuses http fail-fast without the per-run opt-in; the http transport
itself lands in step 7.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QeqhJpYQyghASjiJo5EhGg
2026-07-16 20:02:35 +02:00