AI Implementation feature(1087): Mastodon Post Composition and Publishing (#6)
This commit was merged in pull request #6.
This commit is contained in:
@@ -10,9 +10,8 @@ timestamp: 2026-08-04T17:51:00Z
|
||||
`tenbackward.main` is the entry point executed by cron. The pipeline first
|
||||
ensures that the configured blog repository is available and current, then
|
||||
scans Jekyll posts for today's ten-year anniversary, filters already-recorded
|
||||
paths, and persists newly discovered identifiers. Job 1086 wires the
|
||||
anniversary matcher into the runner; Mastodon publishing remains a future
|
||||
step.
|
||||
paths, publishes a single combined Mastodon status for the new matches, and
|
||||
finally persists the freshly published identifiers.
|
||||
|
||||
|
||||
# Call Flow
|
||||
@@ -28,8 +27,9 @@ main.main()
|
||||
├── result = _run_with_retry(config)
|
||||
│ ├── for attempt in 1 .. max_retries+1:
|
||||
│ │ try: return _run_once(config)
|
||||
│ │ └── _run_once → ensure_repo → iter_anniversary_paths
|
||||
│ │ → PostedStore deduplication → mark_posted_many
|
||||
│ │ └── _run_once → ensure_repo → find_anniversary_matches
|
||||
│ │ → PostedStore deduplication → publish_mastodon
|
||||
│ │ → mark_posted_many # state only after publish OK
|
||||
│ ├── log_error("pipeline_error", exc=exc, attempt=attempt, max_attempts=attempts)
|
||||
│ ├── log_error("pipeline_failed", exc=last_exc, attempts=attempts)
|
||||
│ └── return None
|
||||
@@ -40,12 +40,16 @@ main.main()
|
||||
# Extension Point: `_iter_candidates`
|
||||
|
||||
```python
|
||||
def _iter_candidates(config: Config) -> Iterable[str]: ...
|
||||
def _iter_candidates(config: Config) -> Iterable[MatchedPost]: ...
|
||||
```
|
||||
|
||||
Job 1086 wires `_iter_candidates` to the anniversary matcher. The matcher
|
||||
is now active; it discovers matching Jekyll posts after the repository is
|
||||
synchronized.
|
||||
`_iter_candidates` delegates to `find_anniversary_matches()` with
|
||||
`config.blog_dir / "_posts" / "blog"` and `config.site_url`. The matcher
|
||||
returns full `MatchedPost` values (relative path, title, date, canonical
|
||||
URL); the runner uses `MatchedPost.path` as a stable deduplication ID and
|
||||
passes the full objects to `publish_mastodon()` for status composition.
|
||||
See [Anniversary Matching](/architecture/anniversary-matching.md) for the
|
||||
file and front matter rules.
|
||||
|
||||
## Blog Synchronization
|
||||
|
||||
@@ -56,14 +60,6 @@ fail immediately with `BlogRepoError`. The same synchronization occurs once
|
||||
in `main()` before `startup` and again inside `_run_once()` as the retryable
|
||||
pipeline boundary.
|
||||
|
||||
|
||||
`_iter_candidates(config)` delegates to `iter_anniversary_paths()` with
|
||||
`config.blog_dir / "_posts" / "blog"` and `config.site_url`. The matcher
|
||||
returns relative Markdown paths, such as
|
||||
`2016/2016-08-04-example.md`, which are used as stable deduplication IDs.
|
||||
See [Anniversary Matching](/architecture/anniversary-matching.md) for the
|
||||
file and front matter rules.
|
||||
|
||||
# `_run_once` — Summary Counters
|
||||
|
||||
`_run_once` returns a 5-tuple:
|
||||
@@ -86,6 +82,19 @@ The store persists the list under a `"posted"` key
|
||||
(`{"posted": ["2014/2014-08-04-foo.md", ...]}`) and serialises
|
||||
concurrent runs with an `fcntl.flock`.
|
||||
|
||||
# Publish Boundary
|
||||
|
||||
`publish_mastodon()` is the single success boundary. The runner calls it
|
||||
**after** dedupe and **before** `mark_posted_many`. If the call raises a
|
||||
`PublishError`, the runner treats it like any other pipeline exception —
|
||||
the retry loop in `_run_with_retry` re-attempts, and no identifier is
|
||||
written to `posted.json`. Identical-day matches are published as **one**
|
||||
combined status: prefix line + one `title\nurl` block per post + hashtags
|
||||
line, sorted by `(date, path)` for deterministic ordering. The default
|
||||
prefix is `Heute vor 10 Jahren:` (overridable via `THROWNBACK_PREFIX`).
|
||||
Generated statuses are validated against `MASTODON_STATUS_LIMIT` (500
|
||||
characters); the runner fails safely (raises) rather than truncating.
|
||||
|
||||
# Retry Behaviour (`_run_with_retry`)
|
||||
|
||||
* `attempts = max(1, config.max_retries + 1)` — at least one attempt
|
||||
|
||||
Reference in New Issue
Block a user