Declarative Editorial Context: Decoupling Publishing Targets in the AI Era
Setting the Stage: Context for the Curious Book Reader
When an automated publishing pipeline serves multiple masters—from public book manuscripts and private research notes to internal enterprise work logs—hardcoding editorial assumptions into a single prompt guarantees friction. In this entry, the author tackles prompt architecture by decoupling editorial voice from pipeline mechanics. By declaring a dedicated editorial_context per publishing target in Nix and threading it through JSON into the Python article generator, the system respects the natural tone of corporate updates without diluting the book’s larger themes. Along the way, the workflow demonstrates how small, checkable configuration refactors build psychological momentum and keep multi-target publishing verifiable across every lane.
TL;DR: Editorial framing for journal-to-article conversion is now configurable per publishing target. Each blog declares its own editorial context in the system configuration, which is materialized into the runtime blog configuration and injected into the AI editing prompt before article-specific processing begins.
Technical Journal Entry Begins
🔗 Verified Pipulate Commits:
MikeLev.in: Okay if you get really tired and you have to keep yourself working because there’s some goal you really want to achieve then you have to frame the next thing you do as something you really want and get yourself over that hump and get that second win. It’s the opposite of eating the frog. If you plan it right you won’t feel yourself cooking the frog-of-friction. Friction comes extra energy being spent doing things you don’t want to do. So this is a case for the opposite methodology of Eat That Frog!
Escaping the Monolithic Editorial Frame
In the Age of AI is how all my raw writing journal extracts get turned into articles, and that’s fine for all these book-ore articles I’m writing for Future Proofing Yourself in the Age of AI which is rapidly turning into a book of how to perform Quality Assurance on the output of your AI so that if it’s something important like an air traffic control system or edge programming that changes how your customer’s website is served, you can have the levels of transparency and reproducibility required for certifications.
But I also write for an internal corporate blog where making headlines based around that seems a little weird. I also have this Grimoire blog where if it can’t be public and can’t go on the corporate wiki I can at least put it through the articleizing process. I guess for that one keeping the Future Proofing theme is fine. Point being I want to be able to control it per site the way I control what folder the blog gets posted in and such.
Mapping Editorial Context to Declarative Config
So in blogs.nix I should have something that carries through to the
blogs.json transformation and then along to the publishing pipeline and which
maybe templatizes something in scripts/articles/editing_prompt.txt that isn’t
currently templatized.
I can haz, please and thank you.
The Zero-Surgery Implementation Plan
ChatGPT 6 Extra High: Context verified.
You already have the whole transport path. blogs.nix becomes blogs.json, and articleizer.py already loads the selected target config before assembling editing_prompt.txt. So this needs no publishizer.py surgery: add one editorial_context field per target, one prompt placeholder, and one substitution.
The useful property is that the default preserves today’s Future Proofing behavior, while bot can explicitly say, “this is an internal work journal; don’t force the book theme.”
1. PROBES
The first command is the cheapest falsifying probe: it confirms both that this feature does not exist yet and that the existing target-aware substitution seam is where we think it is. The other two give us the Nix and Python syntax straddle.
(nix) pipulate $
rg -n 'editorial_context|INSERT EDITORIAL CONTEXT|PERMALINK_PATTERN_PLACEHOLDER|BASE_URL_PLACEHOLDER' /home/mike/repos/nixos/blogs.nix scripts/articles/articleizer.py scripts/articles/editing_prompt.txt
nix-instantiate --parse /home/mike/repos/nixos/blogs.nix >/dev/null && printf 'blogs.nix parse OK\n'
.venv/bin/python -c "import ast; from pathlib import Path; ast.parse(Path('scripts/articles/articleizer.py').read_text()); print('articleizer.py AST OK')"
2. NEXT CONTEXT
(nix) pipulate $
/home/mike/repos/nixos/blogs.nix
/home/mike/.config/pipulate/blogs.json
scripts/articles/articleizer.py
scripts/articles/editing_prompt.txt
! rg -n 'editorial_context|INSERT EDITORIAL CONTEXT|PERMALINK_PATTERN_PLACEHOLDER|BASE_URL_PLACEHOLDER' /home/mike/repos/nixos/blogs.nix scripts/articles/articleizer.py scripts/articles/editing_prompt.txt
! nix-instantiate --parse /home/mike/repos/nixos/blogs.nix >/dev/null && printf 'blogs.nix parse OK\n'
! .venv/bin/python -c "import ast; from pathlib import Path; ast.parse(Path('scripts/articles/articleizer.py').read_text()); print('articleizer.py AST OK')"
3. PATCHES
This makes blogs.nix the declarative owner of editorial framing just like it already owns path, permalink prefix, pipeline and preview port.
(nix) pipulate $
Target: /home/mike/repos/nixos/blogs.nix
[[[SEARCH]]]
alias = "article";
[[[DIVIDER]]]
alias = "article";
# Target-specific framing injected into editing_prompt.txt before the article lands.
editorial_context = "This is a public article in Future Proofing Yourself in the Age of AI. Book-oriented framing is welcome when it genuinely fits the article, but prefer the article's actual subject over repetitive series branding.";
[[[REPLACE]]]
(nix) pipulate $
Target: /home/mike/repos/nixos/blogs.nix
[[[SEARCH]]]
alias = "grim";
[[[DIVIDER]]]
alias = "grim";
editorial_context = "This is a private Grimoire entry in the Future Proofing Yourself in the Age of AI writing stream. Book-oriented framing is welcome when it fits, while the article's own subject should still lead the headline.";
[[[REPLACE]]]
(nix) pipulate $
Target: /home/mike/repos/nixos/blogs.nix
[[[SEARCH]]]
alias = "vault";
[[[DIVIDER]]]
alias = "vault";
editorial_context = "This is a private Vault entry. Let the article's own subject and tone lead. Do not impose book-series, Future Proofing, or corporate framing unless the source article itself calls for it.";
[[[REPLACE]]]
(nix) pipulate $
Target: /home/mike/repos/nixos/blogs.nix
[[[SEARCH]]]
alias = "bot";
[[[DIVIDER]]]
alias = "bot";
editorial_context = "This is an internal corporate work-journal article. Prefer direct professional headlines grounded in the work itself. Do not frame the title, description, subheadings, or promotional copy around Future Proofing Yourself in the Age of AI or a book unless the source article explicitly calls for that connection.";
[[[REPLACE]]]
Now give articleizer.py the new template vocabulary.
(nix) pipulate $
Target: scripts/articles/articleizer.py
[[[SEARCH]]]
PERMALINK_PATTERN_PLACEHOLDER = "[INSERT PERMALINK PATTERN]"
BASE_URL_PLACEHOLDER = "[INSERT BASE URL]"
[[[DIVIDER]]]
PERMALINK_PATTERN_PLACEHOLDER = "[INSERT PERMALINK PATTERN]"
BASE_URL_PLACEHOLDER = "[INSERT BASE URL]"
EDITORIAL_CONTEXT_PLACEHOLDER = "[INSERT EDITORIAL CONTEXT]"
[[[REPLACE]]]
The fallback deliberately preserves existing behavior if somebody runs against an old/stale blogs.json. An explicitly empty configured value remains empty because this checks is None, not truthiness.
(nix) pipulate $
Target: scripts/articles/articleizer.py
[[[SEARCH]]]
prompt_template = prompt_template.replace(
BASE_URL_PLACEHOLDER, prompt_base_url.rstrip("/"))
[[[DIVIDER]]]
prompt_template = prompt_template.replace(
BASE_URL_PLACEHOLDER, prompt_base_url.rstrip("/"))
prompt_editorial_context = target_config.get("editorial_context")
if prompt_editorial_context is None:
prompt_editorial_context = (
"This is an article in Future Proofing Yourself in the Age of AI. "
"Book-oriented framing is welcome when it genuinely fits the article."
)
prompt_template = prompt_template.replace(
EDITORIAL_CONTEXT_PLACEHOLDER, str(prompt_editorial_context).strip())
[[[REPLACE]]]
Finally, replace the one global sentence that currently imposes the Age-of-AI framing with a target-owned instruction. This is the important part: otherwise the new field would be fighting an older global command in the same prompt.
(nix) pipulate $
Target: scripts/articles/editing_prompt.txt
[[[SEARCH]]]
Every one of these covers some "critical" or "pivotal" point in the development of, yadda yadda. Don't play that up, it's become repetitive. Instead use language like "interesting" or "important to know in the Age of AI," that sort of thing. It's a tapestry of articles over time one leading into the next, so keep that in mind.
[[[DIVIDER]]]
Every one of these covers some "critical" or "pivotal" point in the development of, yadda yadda. Don't play that up, it's become repetitive.
**Publishing-target editorial context:** [INSERT EDITORIAL CONTEXT]
Let this target-specific context control the framing of the title, description, prepend paragraph, subheadings, title brainstorm, and promotional copy. Do not force a book, corporate, or "Age of AI" angle when the target context says otherwise. The source article remains the authority.
[[[REPLACE]]]
Ignition required: nr. That materializes the new editorial_context values from blogs.nix into ~/.config/pipulate/blogs.json. No other runtime ignition is required; articleizer.py reads that JSON at invocation time.
The resulting control surface is nicely boring:
blogs.nix editorial_context → blogs.json editorial_context → selected target_config → [INSERT EDITORIAL CONTEXT] → editing model.
4. PROMPT
(nix) pipulate $
The per-blog editorial_context patch is applied and nr has materialized blogs.json.
Read the live probe receipts first. Verify that:
1. all four active targets now carry editorial_context through blogs.nix -> blogs.json,
2. articleizer.py substitutes [INSERT EDITORIAL CONTEXT] before its leftover-placeholder airlock,
3. the old globally imposed "important to know in the Age of AI" instruction is gone,
4. target 4 can now generate plain internal corporate headlines without fighting a global Future Proofing instruction.
Then inspect the remaining editing_prompt.txt and articleizer.py language for any OTHER hardwired "book" framing that would materially affect target 4's generated title, description, prepend, or subheadings. Distinguish harmless analysis-schema naming from language that actually reaches the published corporate article. Propose only the smallest next patch if one is genuinely needed.
5. EXTERNAL DELIVERABLES
None. This is entirely inside the declarative publishing configuration and articleizer prompt path.
MikeLev.in: Okay let’s do it.
THE AI-EDIT METHOD
Same commands, run twice, one change between them. Where the readings differ is what the change did; the diff in the middle is the receipt.
1: BEFORE (PROBE):
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean
GIT repo clean. Take BEFORE reading, make CHANGE, record AFTER diff.
(nix) qamyai $ rg -n 'editorial_context|INSERT EDITORIAL CONTEXT|PERMALINK_PATTERN_PLACEHOLDER|BASE_URL_PLACEHOLDER' /home/mike/repos/nixos/blogs.nix scripts/articles/articleizer.py scripts/articles/editing_prompt.txt
nix-instantiate --parse /home/mike/repos/nixos/blogs.nix >/dev/null && printf 'blogs.nix parse OK\n'
.venv/bin/python -c "import ast; from pathlib import Path; ast.parse(Path('scripts/articles/articleizer.py').read_text()); print('articleizer.py AST OK')"
scripts/articles/articleizer.py
58:PERMALINK_PATTERN_PLACEHOLDER = "[INSERT PERMALINK PATTERN]"
59:BASE_URL_PLACEHOLDER = "[INSERT BASE URL]"
600: PERMALINK_PATTERN_PLACEHOLDER,
603: BASE_URL_PLACEHOLDER, prompt_base_url.rstrip("/"))
nix-instantiate: /nix/store/2fxp204b9jh1s3lpggdlnws44vvzw1w9-openssl-3.4.3/lib/libcrypto.so.3: version `OPENSSL_3.6.0' not found (required by /nix/store/1p2gjq4x396yj65rig8fyzhrdza0psd6-openssl-3.6.3/lib/libssl.so.3)
nix-instantiate: /nix/store/i3ibgfskl99qd8rslafbpaa1dmxdzh1z-glibc-2.40-66/lib/libc.so.6: version `GLIBC_ABI_DT_X86_64_PLT' not found (required by /nix/store/ias8xacs1h3jy7xgwi2awvim61k2ji6c-glibc-2.42-67/lib/libresolv.so.2)
articleizer.py AST OK
(nix) qamyai $
2: AFTER (NEXT CONTEXT):
# # Context 1
# ~/repos/nixos/blogs.nix
# ~/.config/pipulate/blogs.json # <-- CAUTION! Derived from ~/repos/nixos/blogs.nix
# scripts/articles/publishizer.py # <-- Orchestrates different publishing workflows per target blog.
# scripts/articles/common.py # <-- Self-explanatory
# scripts/articles/articleizer.py # <-- Transforms raw article.txt to formal Jekyll markdown format
# scripts/articles/editing_prompt.txt # <-- Forcing response into strict JSON data structure
# scripts/articles/sanitizer.py # <-- Scrubs PII
# scripts/articles/gsc_historical_fetch.py
# scripts/articles/contextualizer.py # <-- Builds JSON summaries of articles in `_posts/context/` called "Holographic Shards".
# scripts/articles/confluenceizer.py # <-- Idempotent Jekyll-to-Confluence corporate wiki
# scripts/articles/googledocizer.py # <-- Just added
# scripts/articles/build_knowledge_graph.py # <-- Topically load-balances site using hierarchical K-Means keyword clustering groups
# scripts/articles/generate_ai_context.py # <-- AIs WILL interrogate your repo. This gives epic context of article URLs for drill-down.
# scripts/articles/generate_hubs.py # <-- Uses just-produced link-graph data to generate each of the new hubs it suggests
# scripts/articles/generate_llms_txt.py # <-- Builds an llms.txt based on the auto-organized structure suggested here
# scripts/articles/generate_redirects.py # <-- Generates redirect map above hub-churn suggests is needed
# scripts/articles/sanitize_redirects.py # <-- Deals with follow-up meticulous pedantic detail required for a good Nginx redirect map
# scripts/articles/link_injector.py
# scripts/articles/generate_post_nav.py # [1,174 tokens | 4,616 bytes]
# Context 2
/home/mike/repos/nixos/blogs.nix
/home/mike/.config/pipulate/blogs.json
scripts/articles/articleizer.py
scripts/articles/editing_prompt.txt
! rg -n 'editorial_context|INSERT EDITORIAL CONTEXT|PERMALINK_PATTERN_PLACEHOLDER|BASE_URL_PLACEHOLDER' /home/mike/repos/nixos/blogs.nix scripts/articles/articleizer.py scripts/articles/editing_prompt.txt
! nix-instantiate --parse /home/mike/repos/nixos/blogs.nix >/dev/null && printf 'blogs.nix parse OK\n'
! .venv/bin/python -c "import ast; from pathlib import Path; ast.parse(Path('scripts/articles/articleizer.py').read_text()); print('articleizer.py AST OK')"
3: CHANGE (PATCHES):
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean
GIT repo clean. Take BEFORE reading, make CHANGE, record AFTER diff.
(nix) nixos $ patch
(nix) nixos $ app
↪ Targets resolved from /home/mike/qamyai (PIPULATE_ROOT), not from the folder app was typed in.
✅ PATCH ALREADY APPLIED: '/home/mike/repos/nixos/blogs.nix' already contains the inserted block.
(nix) nixos $ d
(nix) nixos $ git --no-pager show -- /home/mike/repos/nixos/blogs.nix
commit 9531eba16c452f308cc823140b7d652ac6aa90ec (HEAD -> main, origin/main, origin/HEAD)
Author: Mike Levin <[email redacted]>
Date: Tue Oct 6 09:36:00 2026 -0400
chore: Add editorial context to blogs.nix for article framing
diff --git a/blogs.nix b/blogs.nix
index 48ee6c2..fe16e42 100644
--- a/blogs.nix
+++ b/blogs.nix
@@ -81,6 +81,8 @@ let
path = "/home/mike/repos/trimnoir/_posts";
lane = "public";
alias = "article";
+ # Target-specific framing injected into editing_prompt.txt before the article lands.
+ editorial_context = "This is a public article in Future Proofing Yourself in the Age of AI. Book-oriented framing is welcome when it genuinely fits the article, but prefer the article's actual subject over repetitive series branding.";
# OFF THE DAILY COMMIT (2026-09-27). backup-home (b2: the midnight
# timer and the alias alike) commits and pushes every blog in this
# matrix before it mirrors ~/git-repos/ to the SSD, and absent means
(nix) nixos $ patch
(nix) nixos $ app
↪ Targets resolved from /home/mike/qamyai (PIPULATE_ROOT), not from the folder app was typed in.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated '/home/mike/repos/nixos/blogs.nix'.
(nix) nixos $ d
diff --git a/blogs.nix b/blogs.nix
index fe16e42..3e41a92 100644
--- a/blogs.nix
+++ b/blogs.nix
@@ -131,6 +131,7 @@ let
path = "/home/mike/repos/grimoire/_posts";
lane = "private";
alias = "grim";
+ editorial_context = "This is a private Grimoire entry in the Future Proofing Yourself in the Age of AI writing stream. Book-oriented framing is welcome when it fits, while the article's own subject should still lead the headline.";
# THE PORT MOVED AND THIS DID NOT (2026-09-27): the renumber that made
# the Grimoire target 2 moved preview_port to 4002 while this line kept
# saying 4003, which is the Vault now, so a new Grimoire article would
(nix) nixos $ m
📝 Committing: chore: Add editorial context to blogs.nix
[main d3d3c1a] chore: Add editorial context to blogs.nix
1 file changed, 1 insertion(+)
(nix) nixos $ patch
(nix) nixos $ app
↪ Targets resolved from /home/mike/qamyai (PIPULATE_ROOT), not from the folder app was typed in.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated '/home/mike/repos/nixos/blogs.nix'.
(nix) nixos $ d
diff --git a/blogs.nix b/blogs.nix
index 3e41a92..3056fee 100644
--- a/blogs.nix
+++ b/blogs.nix
@@ -148,6 +148,7 @@ let
path = "/home/mike/repos/vault/_posts";
lane = "private";
alias = "vault";
+ editorial_context = "This is a private Vault entry. Let the article's own subject and tone lead. Do not impose book-series, Future Proofing, or corporate framing unless the source article itself calls for it.";
base_url = "";
permalink_prefix = "vault";
preview_port = 4003;
(nix) nixos $ m
📝 Committing: chore: Add editorial context to blogs.nix
[main fd02c34] chore: Add editorial context to blogs.nix
1 file changed, 1 insertion(+)
(nix) nixos $ patch
(nix) nixos $ app
↪ Targets resolved from /home/mike/qamyai (PIPULATE_ROOT), not from the folder app was typed in.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated '/home/mike/repos/nixos/blogs.nix'.
(nix) nixos $ d
diff --git a/blogs.nix b/blogs.nix
index 3056fee..4b9aca1 100644
--- a/blogs.nix
+++ b/blogs.nix
@@ -159,6 +159,7 @@ let
path = "/home/mike/repos/botifyml/_posts";
lane = "public";
alias = "bot";
+ editorial_context = "This is an internal corporate work-journal article. Prefer direct professional headlines grounded in the work itself. Do not frame the title, description, subheadings, or promotional copy around Future Proofing Yourself in the Age of AI or a book unless the source article explicitly calls for that connection.";
base_url = "";
permalink_prefix = "futureproof";
preview_port = 4004;
(nix) nixos $ m
📝 Committing: chore: Update editorial context in blogs.nix
[main 02145e0] chore: Update editorial context in blogs.nix
1 file changed, 1 insertion(+)
(nix) nixos $ h
(nix) qamyai $ n
(nix) nixos $ git push
Enumerating objects: 11, done.
Counting objects: 100% (11/11), done.
Delta compression using up to 48 threads
Compressing objects: 100% (9/9), done.
Writing objects: 100% (9/9), 1.18 KiB | 1.18 MiB/s, done.
Total 9 (delta 6), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (6/6), completed with 2 local objects.
To github.com:miklevin/nixos-config.git
9531eba..02145e0 main -> main
(nix) nixos $ h
(nix) qamyai $ patch
(nix) qamyai $ app
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'scripts/articles/articleizer.py'.
(nix) qamyai $ d
diff --git a/scripts/articles/articleizer.py b/scripts/articles/articleizer.py
index f45e828c..a71b81d9 100644
--- a/scripts/articles/articleizer.py
+++ b/scripts/articles/articleizer.py
@@ -57,6 +57,7 @@ SPINE_PLACEHOLDER = "[INSERT BOOK SPINE]"
# and templating only the folder would hand grimoire the public site's host.
PERMALINK_PATTERN_PLACEHOLDER = "[INSERT PERMALINK PATTERN]"
BASE_URL_PLACEHOLDER = "[INSERT BASE URL]"
+EDITORIAL_CONTEXT_PLACEHOLDER = "[INSERT EDITORIAL CONTEXT]"
# THE FENCES LEAVE FIRST (2026-09-28, the operator's 80/20 after a Workspace
(nix) qamyai $ m
📝 Committing: chore: Add editorial context placeholder
[main 0374771c] chore: Add editorial context placeholder
1 file changed, 1 insertion(+)
(nix) qamyai $ patch
(nix) qamyai $ app
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'scripts/articles/articleizer.py'.
(nix) qamyai $ d
diff --git a/scripts/articles/articleizer.py b/scripts/articles/articleizer.py
index a71b81d9..f7b6e21b 100644
--- a/scripts/articles/articleizer.py
+++ b/scripts/articles/articleizer.py
@@ -602,6 +602,14 @@ def main():
lsa.default_permalink("[slug]", prompt_prefix) + "/")
prompt_template = prompt_template.replace(
BASE_URL_PLACEHOLDER, prompt_base_url.rstrip("/"))
+ prompt_editorial_context = target_config.get("editorial_context")
+ if prompt_editorial_context is None:
+ prompt_editorial_context = (
+ "This is an article in Future Proofing Yourself in the Age of AI. "
+ "Book-oriented framing is welcome when it genuinely fits the article."
+ )
+ prompt_template = prompt_template.replace(
+ EDITORIAL_CONTEXT_PLACEHOLDER, str(prompt_editorial_context).strip())
# THE UNSUBSTITUTED-PLACEHOLDER AIRLOCK: if editing_prompt.txt carries
# a placeholder this file does not know -- a typo, or a template
# patched ahead of its reader -- the literal "[INSERT ...]" reaches the
(nix) qamyai $ m
📝 Committing: chore: Replace editorial context placeholder in articleizer.py
[main b7ea3b6a] chore: Replace editorial context placeholder in articleizer.py
1 file changed, 8 insertions(+)
(nix) qamyai $ patch
(nix) qamyai $ app
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'scripts/articles/editing_prompt.txt'.
(nix) qamyai $ d
diff --git a/scripts/articles/editing_prompt.txt b/scripts/articles/editing_prompt.txt
index 76fe99ac..835982fe 100644
--- a/scripts/articles/editing_prompt.txt
+++ b/scripts/articles/editing_prompt.txt
@@ -9,7 +9,11 @@ You are an AI Content Architect. Your task is not to write a report, but to gene
Use all lower-case and hyphens in permalinks
When describing the passion represented here, you may refer to it as a blueprint, essay, treatise, methodology, philosophy or way. NEVER refer to it as a manifesto which has a negative connotation.
-Every one of these covers some "critical" or "pivotal" point in the development of, yadda yadda. Don't play that up, it's become repetitive. Instead use language like "interesting" or "important to know in the Age of AI," that sort of thing. It's a tapestry of articles over time one leading into the next, so keep that in mind.
+Every one of these covers some "critical" or "pivotal" point in the development of, yadda yadda. Don't play that up, it's become repetitive.
+
+**Publishing-target editorial context:** [INSERT EDITORIAL CONTEXT]
+
+Let this target-specific context control the framing of the title, description, prepend paragraph, subheadings, title brainstorm, and promotional copy. Do not force a book, corporate, or "Age of AI" angle when the target context says otherwise. The source article remains the authority.
Do NOT use "deterministic", "determinism", or "determinacy" in the title, the permalink, the filename, the description, the keywords, the subheadings, or the tweet. The corpus and the spine are full of that word and it is the wrong word: the property these articles care about is that a result can be checked and replayed, not that the same input yields the same output. Reach for "reproducible", "checkable", "replayable", "receipt", "audit trail", or "verifiable" instead. If the article body itself argues about determinism, you may name it once in the prepend paragraph and nowhere else.
**Book Spine (the 40,000-foot view):**
Below is the complete chronological spine of the book-in-progress in two tiers: the deep archive as bare slugs (a complete census of every identity already taken), then the most recent entries in full `date slug | title` detail (the current trajectory of the book). Use the recent tier to fit the ongoing tapestry (one article leading into the next) and the full slug census to avoid repeating angles already covered.
(nix) qamyai $ m
📝 Committing: fix: Add publishing-target editorial context
[main ebea00e8] fix: Add publishing-target editorial context
1 file changed, 5 insertions(+), 1 deletion(-)
(nix) qamyai $ git push
Enumerating objects: 20, done.
Counting objects: 100% (20/20), done.
Delta compression using up to 48 threads
Compressing objects: 100% (15/15), done.
Writing objects: 100% (15/15), 1.76 KiB | 1.76 MiB/s, done.
Total 15 (delta 12), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (12/12), completed with 5 local objects.
To github.com:pipulate/pipulate.git
a9cd5fb6..ebea00e8 main -> main
(nix) qamyai $
Ignition: often “exit”, “nix develop”. Instructions may vary.
(nix) qamyai $ nr
🆕 nixos-unstable moved: 7a0f122 (pinned 2026-09-29T20:45:13Z) -> 151fa4e
Roll forward? Ollama/CUDA will rebuild (about 30 min). [y/N] n
Held at 7a0f122. Roll later with `n --roll`.
🚀 sudo nixos-rebuild switch
building Nix...
building the system configuration...
this derivation will be built:
/nix/store/s1wwydffgig6z8vyspycb25x2pva28ap-nixos-system-nixos-25.05.813814.ac62194c3917.drv
building '/nix/store/s1wwydffgig6z8vyspycb25x2pva28ap-nixos-system-nixos-25.05.813814.ac62194c3917.drv'...
activating the configuration...
setting up /etc...
reloading user units for mike...
restarting sysinit-reactivation.target
the following new units were started: NetworkManager-dispatcher.service
Done. The new configuration is /nix/store/dhs5vy788j8yq1bdl5cq40yxing4zvsd-nixos-system-nixos-25.05.813814.ac62194c3917
(nix) nixos $
4: PROMPT:
The per-blog editorial_context patch is applied and nr has materialized blogs.json.
Read the live probe receipts first. Verify that:
- all four active targets now carry editorial_context through blogs.nix -> blogs.json,
- articleizer.py substitutes [INSERT EDITORIAL CONTEXT] before its leftover-placeholder airlock,
- the old globally imposed “important to know in the Age of AI” instruction is gone,
- target 4 can now generate plain internal corporate headlines without fighting a global Future Proofing instruction.
Then inspect the remaining editing_prompt.txt and articleizer.py language for any OTHER hardwired “book” framing that would materially affect target 4’s generated title, description, prepend, or subheadings. Distinguish harmless analysis-schema naming from language that actually reaches the published corporate article. Propose only the smallest next patch if one is genuinely needed.
5: DELIVERABLE:
Okay, so that’s done. I now just edit a line per blog in blogs.nix and rebuild
and I’ve got a new theme fed to the AI Editor when turning journal extracts into
blog entries. That was easy and it was exactly the sort of “want to do” project
that gets your body back in the habit of doing the same motions as you need to
do for the less invigorating work.
Rekindling Flow Through Frictionless Refactoring
Hop off the ride. This ride’s stated goal is reached – dismount. This is the NOTARY BEAT: the ride ends here, is witnessed here, and is sealed here. Answer all seven beats, briefly:
Verifying Target Isolation and Residual Presentation
- TL;DR: a short, dry, neutral abstract for the TOP of the published article – written for an unfamiliar reader or AI summarizer who has never seen this system. No hype, no insider handles unexplained.
- VERIFY: restate the goal from the top of this article and confirm (or deny) it was met, citing THIS compile’s receipts, not memory. Name any ignition this ride required that never fired – an AFTER tap taken without one is a stale BEFORE wearing the AFTER’s label.
- BANK: name everything that graduates – rule, earmark, todo, pin – as SEARCH/REPLACE patch cars against raw source present in THIS context (Target line inside the fence, one car per commit story), deletions included. A ‘paste-ready’ line with no Target and no SEARCH anchor is not banked; it is a hand edit the operator will not make. These BANK cars are the ONLY patches a dismount emits.
- DANGLING: what carries forward unbanked? One line each, no essays.
- SEED: the context.txt lines (and TODO_SLUGS if narrative context is needed) for the next ride’s first compile.
- CLOSING: a closing summary for the BOTTOM of the article – the final take-away, tied to the book’s larger arc where it fits naturally, never forced. Storytelling over inventory.
- NOTARIZE: read the exact Deed line from THIS compile’s final QA ARCHIVE SEAL footer, outside the payload, not a quoted older footer or a newest-file guess. It names the input archive, not this later response or the generated Jekyll post. State verification separately: only a verifier receipt naming that archive supports a verified claim; otherwise say not independently verified here. Count this Manifest’s live-command receipts, including failures. Quote only recorded digests, sizes and dates, naming their source; filesystem mtime and fixed ZIP dates are not command timestamps. Do not demand absent wc -c or dated-receipt fields. If the footer is missing, say so; otherwise close with its exact Deed line.
FINALITY: after beat 6, this discussion is CLOSED. Emit NO five-car train, NO probes, NO patches beyond the BANK cars of beat 2, and NO next-turn prompt beyond the SEED lines in beat 4. Any reader or model encountering this article later should treat it as a finished, notarized document – an archive entry, not an open thread.
ChatGPT 6 Extra High: Context verified.
1. VERIFY
Met for the stated goal: per-site editorial framing, especially preventing the internal corporate blog from having its headlines forced into the Future Proofing Yourself in the Age of AI theme.
The current compile shows all four target-specific editorial_context values in blogs.nix, including target 4’s explicit instruction to prefer direct professional headlines and avoid Future Proofing/book framing. Pasted text(20261006-134230) The materialized blogs.json contains the same four values, so the declarative value has reached the runtime configuration. Pasted text(20261006-134230)
articleizer.py reads that value, substitutes [INSERT EDITORIAL CONTEXT], and only then runs the unsubstituted-placeholder airlock. Pasted text(20261006-134230) The old global “important to know in the Age of AI” instruction is gone; the prompt now explicitly makes target context authoritative over title, description, prepend, subheadings, brainstorm and promotional copy. Pasted text(20261006-134230)
One evidence caveat: this compile has three live command receipts. The rg receipt succeeded, the Python AST check reports articleizer.py AST OK, and the Nix parse probe failed before producing a parse verdict because of OpenSSL/glibc ABI mismatches. Pasted text(20261006-134230) Pasted text(20261006-134230)
nr was the required ignition, but it is not a live receipt in this compile. I therefore cannot independently claim from this compile that the rebuild command fired; I can only say its intended resulting state is present in the captured blogs.json.
This is not total de-bookification. The prompt still tells the model to write prepend prose for a “curious book reader”, and articleizer.py still emits Curious Book Reader and Book Analysis headings. Pasted text(20261006-134230) Pasted text(20261006-134230) Those are residual presentation concerns, not blockers to the per-site headline/theme control that started this ride.
2. BANK
None. No new rule, earmark, TODO or pin needs to graduate from this dismount, so there is no BANK patch car.
3. DANGLING
The prepend instruction and two assembler headings remain explicitly book-oriented.
The current compile does not contain a successful Nix parse receipt.
The current Codebase does not include confluenceizer.py, so any downstream neutralization of those book-oriented headings is not independently verified here.
4. SEED
/home/mike/repos/nixos/blogs.nix
/home/mike/.config/pipulate/blogs.json
scripts/articles/articleizer.py
scripts/articles/editing_prompt.txt
scripts/articles/confluenceizer.py
5. CLOSING
The useful change was small: editorial identity became data owned by each publishing target instead of an assumption embedded in one global prompt. The public book stream can keep its Future Proofing frame, the private writing can choose its own posture, and the corporate journal can describe the work directly. More importantly, the change reused the existing configuration path rather than creating another publishing mechanism: one declared value now travels from system configuration to the editing model at the point where it matters.
6. NOTARIZE
The Manifest records 3 live-command receipts, including 1 failure. No verifier receipt naming the sealed archive appears among them, so the archive is not independently verified here. The footer nevertheless supplies the exact deed requested. Pasted text(20261006-134230)
Deed: qamy.ai_96-e2554783.zip
Book Analysis
Ai Editorial Take
The most compelling paradigm shift here is treating editorial voice as infrastructure configuration rather than prompt engineering. In standard generative AI workflows, voice is treated as conversational nuance or prompt-template sprawl. By moving editorial framing into blogs.nix alongside preview ports and filesystem paths, prompt instructions become declarative environmental metadata. The language model is relegated to a stateless rendering engine governed by host-level configurations, making tone and persona fully auditable across multi-tenant publication pipelines.
🐦 X.com Promo Tweet
One prompt cannot rule them all. How to decouple editorial framing across book streams, internal corporate blogs, and private notes using declarative Nix and Python.
https://mikelev.in/futureproof/declarative-editorial-framing-pipeline/
#NixOS #Python #AI
Title Brainstorm
- Title Option: Declarative Editorial Context: Decoupling Publishing Targets in the AI Era
- Filename:
declarative-editorial-framing-pipeline.md - Rationale: Directly identifies the architectural breakthrough of defining editorial posture per target in declarative configuration.
- Filename:
- Title Option: The Declarative Editorial Airlock: Per-Target Framing for AI Publishing
- Filename:
declarative-editorial-airlock-publishing.md - Rationale: Focuses on the pipeline safety mechanism that passes target-specific context through configuration into prompt assembly without code surgery.
- Filename:
- Title Option: Decoupling Voice from Transport: Configuring Editorial Context Per Target
- Filename:
decoupling-voice-from-transport-editorial-context.md - Rationale: Emphasizes the separation of concerns between shared publishing machinery and individualized publication voices.
- Filename:
- Title Option: Un-Hardwiring Editorial Voice: Declarative Prompt Framing Across Publishing Streams
- Filename:
unhardwiring-editorial-voice-prompt-framing.md - Rationale: Highlights the elimination of monolithic global prompt assumptions in favor of modular declarative overrides.
- Filename:
Content Potential And Polish
- Core Strengths:
- Concrete, reproducible problem formulation: clearly identifies the friction caused by forcing enterprise logs into a public book-series tone.
- Elegant zero-surgery architecture: reuses the existing transport path (blogs.nix to blogs.json to articleizer.py) without modifying publishizer.py.
- Verifiable execution discipline: rigorous use of the before-and-after AI-edit protocol with recorded diffs and AST checks.
- Honest audit reporting: the Notary Beat candidly notes the Nix parse probe ABI mismatch and remaining presentation-layer book headings.
- Suggestions For Polish:
- Address residual presentation scaffolding: decouple hardcoded template headers like ‘Curious Book Reader’ and ‘Book Analysis’ in articleizer.py to achieve complete voice neutrality for corporate targets.
- Resolve Nix store ABI divergence: fix the OpenSSL and glibc dynamic linking mismatch to ensure nix-instantiate parse verification passes cleanly in future compiles.
- Clarify downstream consumers: verify whether corporate exporters like confluenceizer.py filter or strip book-specific markdown structures before publishing to internal wikis.
Next Step Prompts
- Draft a patch for articleizer.py that parameterizes the section headers (‘Setting the Stage’ and ‘Book Analysis’) so corporate targets can render neutral professional headers like ‘Executive Summary’ and ‘Technical Notes’.
- Inspect confluenceizer.py to verify whether corporate wiki publishing correctly consumes the new target-specific editorial context and strips out any remaining public book artifacts.