Skip to content

spec/018: adoption readiness — README and onboarding#33

Draft
outergod wants to merge 35 commits into
masterfrom
018-adoption-readiness
Draft

spec/018: adoption readiness — README and onboarding#33
outergod wants to merge 35 commits into
masterfrom
018-adoption-readiness

Conversation

@outergod
Copy link
Copy Markdown
Owner

@outergod outergod commented May 6, 2026

Summary

  • Restructures README around operational comprehension (FR-001 12-section ordering).
  • Adds Mermaid architecture diagram + prose fallback for non-rendering contexts.
  • Adds docs/onboarding-script.sh regeneration entry point for the (forthcoming) docs/onboarding.cast.

Status

Draft. Landed: T001–T010 (US1 MVP), T003 (foundational), T017–T018 (US3, pending render verification on this preview).
Pending: T011–T016 (US2 walkthrough + cast recording), T020 (US4 checklist), T021–T028 (polish + dogfooding + ready-for-review).

Spec: specs/018-adoption-readiness/spec.md · Tasks: tasks.md

Test plan

  • Mermaid block renders as a diagram on the Files tab (T019)
  • core-ops-release validate --base-ref master passes
  • FR-017 invariant: no src/ / tests/ / examples/ / LICENSE / Cargo.lock / .github/workflows/ modifications
  • FR-014 stop-list grep returns zero matches

🤖 Generated with Claude Code

outergod added a commit that referenced this pull request May 6, 2026
Branch pushed to origin/018-adoption-readiness; draft PR opened at
#33. Operator confirmed the
Mermaid block renders as a diagram on the Files tab (not a raw fenced
code block), satisfying T019's pass criterion. A small `nodeSpacing`
tweak in 40001bb addresses GitHub's zoom/pan control overlap with the
rightmost node.

Closes out US3 (T017–T019). US2 (walkthrough + cast) and remaining
phases (US4, Polish) are next.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
outergod added a commit that referenced this pull request May 6, 2026
Four integration tests under tests/integration/ asserted on README section
names from the pre-spec/018 layout (## What is CoreOps?, ## Credibility,
## Installation (Current Phase)) and one fixture pinned the controller
version at 2.2.0. After the README restructure (T004-T009) and the
2.2.0 -> 2.2.1 bump driven by packaged_readme_surface, all four turned
red on PR #33's CI:

- test_distribution_credibility::credibility_surface_matches_release_metadata_fixture
- test_distribution_entrypoint::readme_contains_required_entrypoint_sections
- test_distribution_installation::readme_documents_binary_installation_sequence
- test_status_contract::controller_version_provenance_matches_cargo_package_version

This is mechanical fixture maintenance: the assertions track section
names and a version string, neither of which represents new test surface
or new behavioral coverage. FR-017's original prohibition on tests/
modifications was authored without anticipating that the README rename
would propagate into existing structural assertions, and is relaxed
here in the same shape as the prior packaged_readme_surface carve-out
(decision_018-packaged-readme-surface-cargo-bump).

Spec changes:
- spec.md FR-017: tests/integration/ and tests/fixtures/ MAY be updated
  for FR-001 section renames or for the bumped Cargo.toml version. New
  tests, new fixtures, and new behavioral assertions remain prohibited.
- spec.md SC-010: matching adjustment; Cargo.toml/Cargo.lock/tests/
  excluded from the no-source-touched diff check.
- tasks.md: Tests-Exempted, Path-conventions, T024, and Notes updated
  to reflect the carve-out.

Test changes:
- test_distribution_credibility.rs: ## Credibility -> ## Trust and
  release model (FR-001 §10 fold). Snapshot-fixture assertions track
  the new FR-001 section names.
- test_distribution_entrypoint.rs: heading list replaced with the
  FR-001 12-section ordering.
- test_distribution_installation.rs: ## Installation (Current Phase)
  -> ## Quick start (FR-001 §7 fold).
- valid-success.json: controller.version 2.2.0 -> 2.2.1 (matches
  Cargo.toml).
- entrypoint-snapshot.md: section list updated to track the FR-001
  ordering.

Notably untouched: tests/fixtures/distribution/release-metadata.json's
credibility_location field still points at the removed
README.md#credibility anchor. Modifying that fixture would trigger
machine_readable_distribution_contract (major bump) which is out of
scope for spec/018's patch budget; flagged as a follow-up.

Verification:
$ cargo test                                                                  472 passed
$ cargo clippy --all-targets -- -D warnings                                   clean
$ cargo run --bin core-ops-release -- validate --base-ref master              passed (patch)
$ git diff master..HEAD --stat -- src/ .github/workflows/ examples/ LICENSE   empty

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
outergod added a commit that referenced this pull request May 7, 2026
Branch pushed to origin/018-adoption-readiness; draft PR opened at
#33. Operator confirmed the
Mermaid block renders as a diagram on the Files tab (not a raw fenced
code block), satisfying T019's pass criterion. A small `nodeSpacing`
tweak in 40001bb addresses GitHub's zoom/pan control overlap with the
rightmost node.

Closes out US3 (T017–T019). US2 (walkthrough + cast) and remaining
phases (US4, Polish) are next.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
outergod added a commit that referenced this pull request May 7, 2026
Four integration tests under tests/integration/ asserted on README section
names from the pre-spec/018 layout (## What is CoreOps?, ## Credibility,
## Installation (Current Phase)) and one fixture pinned the controller
version at 2.2.0. After the README restructure (T004-T009) and the
2.2.0 -> 2.2.1 bump driven by packaged_readme_surface, all four turned
red on PR #33's CI:

- test_distribution_credibility::credibility_surface_matches_release_metadata_fixture
- test_distribution_entrypoint::readme_contains_required_entrypoint_sections
- test_distribution_installation::readme_documents_binary_installation_sequence
- test_status_contract::controller_version_provenance_matches_cargo_package_version

This is mechanical fixture maintenance: the assertions track section
names and a version string, neither of which represents new test surface
or new behavioral coverage. FR-017's original prohibition on tests/
modifications was authored without anticipating that the README rename
would propagate into existing structural assertions, and is relaxed
here in the same shape as the prior packaged_readme_surface carve-out
(decision_018-packaged-readme-surface-cargo-bump).

Spec changes:
- spec.md FR-017: tests/integration/ and tests/fixtures/ MAY be updated
  for FR-001 section renames or for the bumped Cargo.toml version. New
  tests, new fixtures, and new behavioral assertions remain prohibited.
- spec.md SC-010: matching adjustment; Cargo.toml/Cargo.lock/tests/
  excluded from the no-source-touched diff check.
- tasks.md: Tests-Exempted, Path-conventions, T024, and Notes updated
  to reflect the carve-out.

Test changes:
- test_distribution_credibility.rs: ## Credibility -> ## Trust and
  release model (FR-001 §10 fold). Snapshot-fixture assertions track
  the new FR-001 section names.
- test_distribution_entrypoint.rs: heading list replaced with the
  FR-001 12-section ordering.
- test_distribution_installation.rs: ## Installation (Current Phase)
  -> ## Quick start (FR-001 §7 fold).
- valid-success.json: controller.version 2.2.0 -> 2.2.1 (matches
  Cargo.toml).
- entrypoint-snapshot.md: section list updated to track the FR-001
  ordering.

Notably untouched: tests/fixtures/distribution/release-metadata.json's
credibility_location field still points at the removed
README.md#credibility anchor. Modifying that fixture would trigger
machine_readable_distribution_contract (major bump) which is out of
scope for spec/018's patch budget; flagged as a follow-up.

Verification:
$ cargo test                                                                  472 passed
$ cargo clippy --all-targets -- -D warnings                                   clean
$ cargo run --bin core-ops-release -- validate --base-ref master              passed (patch)
$ git diff master..HEAD --stat -- src/ .github/workflows/ examples/ LICENSE   empty

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@outergod outergod force-pushed the 018-adoption-readiness branch from f022319 to 6433b50 Compare May 7, 2026 05:29
outergod added a commit that referenced this pull request May 7, 2026
Branch pushed to origin/018-adoption-readiness; draft PR opened at
#33. Operator confirmed the
Mermaid block renders as a diagram on the Files tab (not a raw fenced
code block), satisfying T019's pass criterion. A small `nodeSpacing`
tweak in 40001bb addresses GitHub's zoom/pan control overlap with the
rightmost node.

Closes out US3 (T017–T019). US2 (walkthrough + cast) and remaining
phases (US4, Polish) are next.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
outergod added a commit that referenced this pull request May 7, 2026
Four integration tests under tests/integration/ asserted on README section
names from the pre-spec/018 layout (## What is CoreOps?, ## Credibility,
## Installation (Current Phase)) and one fixture pinned the controller
version at 2.2.0. After the README restructure (T004-T009) and the
2.2.0 -> 2.2.1 bump driven by packaged_readme_surface, all four turned
red on PR #33's CI:

- test_distribution_credibility::credibility_surface_matches_release_metadata_fixture
- test_distribution_entrypoint::readme_contains_required_entrypoint_sections
- test_distribution_installation::readme_documents_binary_installation_sequence
- test_status_contract::controller_version_provenance_matches_cargo_package_version

This is mechanical fixture maintenance: the assertions track section
names and a version string, neither of which represents new test surface
or new behavioral coverage. FR-017's original prohibition on tests/
modifications was authored without anticipating that the README rename
would propagate into existing structural assertions, and is relaxed
here in the same shape as the prior packaged_readme_surface carve-out
(decision_018-packaged-readme-surface-cargo-bump).

Spec changes:
- spec.md FR-017: tests/integration/ and tests/fixtures/ MAY be updated
  for FR-001 section renames or for the bumped Cargo.toml version. New
  tests, new fixtures, and new behavioral assertions remain prohibited.
- spec.md SC-010: matching adjustment; Cargo.toml/Cargo.lock/tests/
  excluded from the no-source-touched diff check.
- tasks.md: Tests-Exempted, Path-conventions, T024, and Notes updated
  to reflect the carve-out.

Test changes:
- test_distribution_credibility.rs: ## Credibility -> ## Trust and
  release model (FR-001 §10 fold). Snapshot-fixture assertions track
  the new FR-001 section names.
- test_distribution_entrypoint.rs: heading list replaced with the
  FR-001 12-section ordering.
- test_distribution_installation.rs: ## Installation (Current Phase)
  -> ## Quick start (FR-001 §7 fold).
- valid-success.json: controller.version 2.2.0 -> 2.2.1 (matches
  Cargo.toml).
- entrypoint-snapshot.md: section list updated to track the FR-001
  ordering.

Notably untouched: tests/fixtures/distribution/release-metadata.json's
credibility_location field still points at the removed
README.md#credibility anchor. Modifying that fixture would trigger
machine_readable_distribution_contract (major bump) which is out of
scope for spec/018's patch budget; flagged as a follow-up.

Verification:
$ cargo test                                                                  472 passed
$ cargo clippy --all-targets -- -D warnings                                   clean
$ cargo run --bin core-ops-release -- validate --base-ref master              passed (patch)
$ git diff master..HEAD --stat -- src/ .github/workflows/ examples/ LICENSE   empty

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@outergod outergod force-pushed the 018-adoption-readiness branch from bce0d5b to f64c284 Compare May 7, 2026 08:09
outergod and others added 22 commits May 7, 2026 12:43
Author specs/018-adoption-readiness/spec.md targeting README + onboarding
adoption readiness post-spec/017. Four prioritized user stories (P1: 5-min
mental simulation; P2: walkthrough credibility; P2: architecture diagram;
P3: structural contract for future maintainers). Nineteen functional
requirements split across README structure (FR-001..003), onboarding
artifacts (FR-004..009a), tone preservation (FR-010..014), Spec Kit
deliverables (FR-015..016), and explicit negative requirements
(FR-017..019). Twelve+ structural success criteria (grep / wc /
file-existence) plus an author self-attested dogfooding pass.

Five clarifications integrated in Session 2026-05-06:

  - Recording sanitization mirrors spec/017 FR-009 (RFC 2606 / RFC 5737,
    generic prompt, no operator-private values).
  - Hard cap of 90 seconds on docs/onboarding.cast; narrow scope rather
    than extend.
  - Walkthrough plan-output blocks are verbatim from real invocations,
    `...` elision permitted, no paraphrasing or hand-tuning.
  - Dogfooding pass via author self-attestation (acknowledged limit
    traded against feasibility for solo-author project).
  - Static walkthrough carries plan + idempotent re-run snippet (~25
    lines); apply / status are recording-only.

Release fragment declares release_intent: patch, scope: docs.
Validates as exempt under always_exempt_documentation_or_formatting.
Branched from master at v2.2.0 (cfc0914).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 0 / Phase 1 outputs from /speckit.plan workflow:

  - plan.md: Technical Context, Constitution Check (PASS — VM-backed
    scenario exemption recorded), project structure decision,
    Phase 0 / 1 summaries, Complexity Tracking (no entries), and post-
    design constitution re-check.
  - research.md: five resolved decisions (R1 asciinema tooling pin,
    R2 Mermaid GitHub render fidelity, R3 README size benchmarks,
    R4 sanitization at recording time via env-scrubbed shell, R5
    walkthrough fidelity verification at PR review). Three follow-ups
    captured (static SVG sidecar, CONTRIBUTING/ARCHITECTURE follow-up,
    multi-language docs) — none blocking.
  - quickstart.md: one-page operator pointer covering what 018 changes
    on disk, what stays unchanged, local verification commands,
    re-recording instructions, and relationship to spec/017.

data-model.md and contracts/ are explicitly omitted per spec FR-015 —
no new data structures or CLI contracts are introduced.

CLAUDE.md regenerated via .specify/scripts/bash/update-agent-context.sh
to reflect the (minimal) tech-stack delta.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
28 tasks across 7 phases:

  - Phase 1 Setup: 2 parallel scaffold tasks (checklist + synthesis
    skeletons).
  - Phase 2 Foundational: docs/onboarding-script.sh regeneration entry
    point with version-pinned asciinema and env-scrubbed shell.
  - Phase 3 (US1, P1, MVP): 7 tasks restructuring README into the
    canonical 12-section ordering, promoting badge row, authoring the
    30-second mental model, compressing philosophy, folding Trust
    section, and inserting placeholders for US2/US3 fills.
  - Phase 4 (US2, P2): 6 tasks capturing verbatim plan + idempotent
    re-run output, populating the walkthrough section, recording the
    cast (≤ 90 s), sanitizing, and linking from README.
  - Phase 5 (US3, P2): 3 tasks authoring the Mermaid block, the
    non-Mermaid prose fallback, and verifying GitHub render.
  - Phase 6 (US4, P3): 1 task populating the structural checklist
    runbook with one runnable command per FR/SC.
  - Phase 7 Polish: 8 tasks (4 parallel structural checks, dogfooding
    pass after 24h cool-off, synthesis authoring, release-governance
    re-validation, PR open).

Tests are exempted per spec FR-019. Rust gates (cargo test / clippy)
remain authoritative for the codebase but are not exercised by this
docs-only iteration.

US1 is the MVP — restructured README is shippable as a draft PR even
before US2/US3 fill the placeholders.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolves the four MEDIUM-severity findings from the post-tasks
analysis pass:

  - F1 (spec.md Edge Case #3): replace stale "FR-008" reference with
    "FR-006" — the verbatim-from-real-invocation rule is the actual
    mitigation for walkthrough drift.
  - F2 (spec.md Edge Case #3): replace stale "Phase 5" reference with
    "polish phase / Phase 7" — re-validation lives in tasks.md
    Phase 7 (T021/SC-007a), not Phase 5 (US3 Mermaid).
  - F3 (tasks.md T011/T012): drop [P] from T012 and document the
    serialization rationale — apply mutates `--host immich` state
    that T011's pre-apply plan reads. Sequential by default;
    parallel only on isolated hosts. Updated Phase 4 dependency
    note and Parallel Opportunities accordingly.
  - F4 (spec.md US2 Independent Test + Why-this-priority): update
    "the static block" (singular) phrasing to reflect post-clarify
    FR-006 two-block structure (plan + idempotent re-run).

LOW findings (F5–F10) deferred — cosmetic / acknowledged-tradeoff /
stale-on-arrival line references that don't affect implementation.

No constitution alignment changes, no new FRs/SCs, no new tasks.
Validation re-passes as exempt under
always_exempt_documentation_or_formatting.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Empty skeleton for the post-implementation runbook used by US4 (T020)
to verify the structural contract at PR-review time. Pre-populated
with the FR/SC index for each check; commands themselves are filled
later by T020 once US1/US2/US3 land and the final README shape
stabilizes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Skeleton retrospective with section headers for the dogfooding pass
(T025), the proposal §9 questions (T026), the follow-up candidates
flagged in research.md, and a "judgment calls" section to capture
non-trivial implementer decisions the spec did not pre-resolve
(e.g., placement of existing-but-not-canonical sections like
"What CoreOps Does" and "Supported Systems").

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Structural reorder only; content preserved verbatim per task scope.
Subsequent tasks fill the new sections (T005 badge promotion,
T006 mental model, T007 philosophy compression, T008 Trust fold,
T009 Architecture/Walkthrough placeholders).

  - Move Real-world examples (was line 115, FR-012) above Quick start.
  - Fold Installation (Current Phase) + First Interaction +
    Supported Systems into one ## Quick start section. Supported
    Systems becomes a ### subsection of Quick start.
  - Move Why CoreOps exists / What CoreOps is not down to §8/§9.
  - Move Credibility, Minimal Trust Story, Release & Verification
    Model adjacent to each other in §10 area for T008 to fold.
  - Lowercase headings: AI authorship, Why CoreOps exists,
    What CoreOps is not, Real-world examples, Further reading,
    Target audience.
  - Consolidate Target Audience + License + Further Reading into
    one ## Target audience · License · Further reading section.
  - Insert HTML comments where T006/T009 will fill new sections.
  - Legacy "What is CoreOps?" + "What CoreOps Does" kept in place
    for T006 to absorb into the 30-second mental model.

README: 275 → 264 lines.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per FR-002 / SC-002:

  - Badge row moved to immediately after the title block (centered,
    inline HTML to match the existing logo + tagline style).
  - Added 4th badge: License (AGPL-3.0-or-later) linking to LICENSE.
  - Final badge row order: CI · E2E Gate · Latest Release · License.
  - Dissolved the "## Credibility" heading; the signal table and
    blurb remain in their current position for T008 to fold into
    "## Trust and release model".

Used inline <p align="center"> + <a><img> markup (matching the
existing logo/tagline pattern) instead of shields.io badge markdown
to keep the badges centered and grouped on a single visual line.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per FR-001 §3 / SC-003. ~150 words (under the 200-word cap).
Covers all four required topics: host-native convergence,
systemd/Quadlet centricity, declarative reconciliation,
Git-driven operation. Heading at line 20 (within the 120-line
target).

Replaces the legacy "## What is CoreOps?" introduction and
absorbs the "## What CoreOps Does" feature bullets into the
plan/apply/status command beats. Net README change: 269 → 263
lines (-6).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per FR-001 §8/§9/§11 budgets. Final section line counts (heading +
content, excluding the trailing --- separator):

  - Why CoreOps exists: 11 lines (budget ≤ 15)
  - What CoreOps is not:  8 lines (budget ≤ 12)
  - AI authorship:       12 lines (budget ≤ 12)

Why CoreOps exists collapsed two short bulleted paragraphs into one
prose paragraph: same content, no semantic loss, drops 4 lines.

What CoreOps is not and AI authorship were already within budget;
no edits required for them.

README: 263 → 257 lines.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per FR-001 §10. Three previously separate sections collapsed into
one cohesive ## Trust and release model:

  - Credibility table (artifacts row + verification environment row)
  - Minimal Trust Story (audit-surface story)
  - Release & Verification Model (release-gate / promote story)

Verification environment row preserved verbatim
(`fedora-coreos-self-hosted@2026-04-fcos`).

Bullet lists collapsed into prose where the bullets were short enough
to read more naturally as a sentence (audit-surface enumeration in
the first paragraph; spec/scenarios/gate enumeration in the third).
The release-job description now reads as one paragraph instead of
three sub-paragraphs.

README: 257 → 225 lines (-32). Section ordering now matches FR-001
for §1, §3, §6–§12; §4 Architecture and §5 Walkthrough are still
HTML-comment placeholders pending T009.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Insert proper ## Architecture (§4) and ## What using CoreOps feels
like (§5) headings in their canonical positions, with blockquoted
"[Populated by USx implementation (tasks Tnnn-Tnnn)]" notes naming
exactly which user story / tasks fill each section.

After this commit, README has all 12 canonical sections of FR-001
in order: §1 Title (block), §2 Badge row (no heading), §3 Mental
model, §4 Architecture (placeholder), §5 Walkthrough (placeholder),
§6 Real-world examples, §7 Quick start, §8 Why CoreOps exists,
§9 What CoreOps is not, §10 Trust and release model,
§11 AI authorship, §12 Target audience · License · Further reading.

The README is now structurally compliant with FR-001 ordering;
content for §4 and §5 will be filled by US3 (T017–T019) and
US2 (T011–T016) respectively in subsequent sessions.

README: 225 → 243 lines (+18, all in placeholder blockquotes).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Read-only verification against post-T009 README.md. All structural
checks pass:

  SC-001 README line budget:      243 lines (≤ 400) — PASS
  SC-003 mental model heading:    line 20 (≤ 120)  — PASS
  SC-008 hype stop-list:          0 matches        — PASS
  Compatibility: 9 pre-018 link targets all resolve — PASS

All 12 canonical sections of FR-001 present and ordered correctly:
  §1  Title (block)
  §2  Badge row (no heading)
  §3  ## 30-second mental model
  §4  ## Architecture                        (placeholder)
  §5  ## What using CoreOps feels like       (placeholder)
  §6  ## Real-world examples
  §7  ## Quick start
  §8  ## Why CoreOps exists
  §9  ## What CoreOps is not
  §10 ## Trust and release model
  §11 ## AI authorship
  §12 ## Target audience · License · Further reading

US1 MVP complete. The README is shippable as a draft PR; the
Architecture and Walkthrough placeholders explicitly call out
which user stories will fill them.

Release governance still passes (exempt under
always_exempt_documentation_or_formatting).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Background: The README modifications landed in T004–T009 trigger the
release-governance rule `packaged_readme_surface` because README.md
ships in published release bundles per the Quick Start documentation.
The rule (correctly) requires a Cargo.toml patch bump to keep the
packaged README's "this is the version you're getting" promise honest.

Spec/018 FR-017 was authored without anticipating this rule and
forbade Cargo.toml modifications outright. That over-restriction is
relaxed here — Cargo.toml MAY be patch-bumped when triggered by
release-governance rules; the bump is metadata-only and does not
constitute a source-code change. Cargo.lock follows.

Changes:

  - Cargo.toml: version 2.2.0 → 2.2.1
  - Cargo.lock: regenerated (cargo update --workspace --offline)
  - spec.md FR-017: relax to permit governance-driven Cargo.toml bumps
  - spec.md SC-010: matching adjustment

`core-ops-release validate --base-ref master` now reports:
  Outcome: passed | Classification: releasable
  Required bump: patch | Declared bump: patch | Version bump: patch
  CHANGELOG aligned: yes

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds `asciinema` 2.4.0 (nixpkgs-pinned) to the dev shell so
`docs/onboarding-script.sh` (T003) can re-record `docs/onboarding.cast`
(T014) without an out-of-shell install. Version is asserted in the
script header per spec/018 R1 + FR-009.

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

The spec consistently referenced `--host immich` for the canonical
walkthrough command, but `examples/03-immich/hosts/example/host.yaml`
declares `host: example` — the only host overlay in the example.
`--host immich` does not resolve and would have produced a fabricated
walkthrough block, violating FR-006's verbatim-from-real-invocation
rule. Aligns with the existing README convention (line 81 already uses
`--host example` for examples/01-caddy-whoami).

Mechanical s/--host immich/--host example/g across spec.md, tasks.md,
research.md, quickstart.md — 11 occurrences, no semantic change beyond
the host name.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Regeneration entry point for `docs/onboarding.cast` (T014). Records
the spec/017 stateless `--source-repo` workflow against
`examples/03-immich --host example`: initial plan, apply, idempotent
re-run.

Sanitization (FR-009a, mirrors spec/017 FR-009) is enforced at
recording time via an env-scrubbed `bash --noprofile --norc` subshell:

  env -i HOME=/home/op PATH=/usr/local/bin:/usr/bin:/bin
        TERM=xterm-256color PS1='op@example $ '

The SC-006a stop-list grep is intentionally NOT embedded in this
script — putting the regex in the file would self-match. The check
lives in the spec's structural checklist (T020) and is run from there.

Pinned to asciinema 2.4.0 (nix devshell pin from prior commit). The
script header documents operator prerequisites including the
GPU-passthrough edge case for `examples/03-immich/services/immich-ml`.

Flips T003 in tasks.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the US3 placeholder in README ## Architecture with a
left-to-right flowchart of the five primary nodes:

  GIT → CORE → STATE → HOST    (primary convergence flow)
  CORE -.-> AUDIT, HOST -.-> AUDIT    (audit + status side outputs,
                                       dashed to signal secondary)

Verified: ≥4 nodes (5), substrings `Git` / `core-ops` / `systemd`
present, audit edges dashed (FR-004 / SC-004). Render verification
on the GitHub PR preview is T019.

Surrounding prose for non-Mermaid renderers comes in T018.

Flips T017 in tasks.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a 5-line "read left-to-right" paragraph after the Mermaid block
that names all four primary nodes (`Git repository`, `core-ops`,
`systemd + Quadlet units`, `host`) and the side output (`audit +
status`) in plain text. Per US3-AC-3, the architecture must be
recoverable on render contexts that do not display Mermaid (RSS,
mirrors, terminal viewers like \`glow\`).

Flips T018 in tasks.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GitHub's Mermaid player overlays zoom/pan/fullscreen controls in the
top-right corner of the rendered diagram. With the rightmost `HOST`
node positioned tight against the edge, the controls cover it on
initial render. Adds a `%%{init}%%` directive bumping `nodeSpacing`
to 50 and `rankSpacing` to 70 so the layout fits with margin.

GitHub honors `init` directives in Mermaid blocks; if a future
renderer strips them, the diagram falls back to defaults — no
content change, only spacing.

FR-004 invariants reverified: ≥4 nodes (5), substrings `Git` /
`core-ops` / `systemd` present, audit edges remain dashed.
Branch pushed to origin/018-adoption-readiness; draft PR opened at
#33. Operator confirmed the
Mermaid block renders as a diagram on the Files tab (not a raw fenced
code block), satisfying T019's pass criterion. A small `nodeSpacing`
tweak in 40001bb addresses GitHub's zoom/pan control overlap with the
rightmost node.

Closes out US3 (T017–T019). US2 (walkthrough + cast) and remaining
phases (US4, Polish) are next.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four integration tests under tests/integration/ asserted on README section
names from the pre-spec/018 layout (## What is CoreOps?, ## Credibility,
## Installation (Current Phase)) and one fixture pinned the controller
version at 2.2.0. After the README restructure (T004-T009) and the
2.2.0 -> 2.2.1 bump driven by packaged_readme_surface, all four turned
red on PR #33's CI:

- test_distribution_credibility::credibility_surface_matches_release_metadata_fixture
- test_distribution_entrypoint::readme_contains_required_entrypoint_sections
- test_distribution_installation::readme_documents_binary_installation_sequence
- test_status_contract::controller_version_provenance_matches_cargo_package_version

This is mechanical fixture maintenance: the assertions track section
names and a version string, neither of which represents new test surface
or new behavioral coverage. FR-017's original prohibition on tests/
modifications was authored without anticipating that the README rename
would propagate into existing structural assertions, and is relaxed
here in the same shape as the prior packaged_readme_surface carve-out
(decision_018-packaged-readme-surface-cargo-bump).

Spec changes:
- spec.md FR-017: tests/integration/ and tests/fixtures/ MAY be updated
  for FR-001 section renames or for the bumped Cargo.toml version. New
  tests, new fixtures, and new behavioral assertions remain prohibited.
- spec.md SC-010: matching adjustment; Cargo.toml/Cargo.lock/tests/
  excluded from the no-source-touched diff check.
- tasks.md: Tests-Exempted, Path-conventions, T024, and Notes updated
  to reflect the carve-out.

Test changes:
- test_distribution_credibility.rs: ## Credibility -> ## Trust and
  release model (FR-001 §10 fold). Snapshot-fixture assertions track
  the new FR-001 section names.
- test_distribution_entrypoint.rs: heading list replaced with the
  FR-001 12-section ordering.
- test_distribution_installation.rs: ## Installation (Current Phase)
  -> ## Quick start (FR-001 §7 fold).
- valid-success.json: controller.version 2.2.0 -> 2.2.1 (matches
  Cargo.toml).
- entrypoint-snapshot.md: section list updated to track the FR-001
  ordering.

Notably untouched: tests/fixtures/distribution/release-metadata.json's
credibility_location field still points at the removed
README.md#credibility anchor. Modifying that fixture would trigger
machine_readable_distribution_contract (major bump) which is out of
scope for spec/018's patch budget; flagged as a follow-up.

Verification:
$ cargo test                                                                  472 passed
$ cargo clippy --all-targets -- -D warnings                                   clean
$ cargo run --bin core-ops-release -- validate --base-ref master              passed (patch)
$ git diff master..HEAD --stat -- src/ .github/workflows/ examples/ LICENSE   empty

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
outergod and others added 8 commits May 7, 2026 12:43
…n blocks

T011 (initial plan capture) and T012 (apply + idempotent re-run capture)
were exercised end-to-end against `core-ops-uat` (Fedora CoreOS guest on
ulthar libvirt). Apply succeeded after PR #34's postgres image-tag fix
landed and after the operator-prereq podman secret `immich-db-password`
was created on the host (per `examples/03-immich/README.md`).

T013 replaces the `## What using CoreOps feels like` placeholder with
two verbatim fenced code blocks per FR-006:

- Plan output for `core-ops plan --source-repo examples/03-immich
  --host example` on a clean host: header + `[+] Create • 10` +
  `immich-database.container` entry with its `requires` graph and
  diff fragment + `...` elision + summary footer.
- Idempotent re-run after `core-ops apply` converged the host:
  `[·] Unchanged • 10` + three representative service entries +
  `...` + `10 unchanged` summary.

Every non-elided line appears byte-for-byte in actual core-ops output;
`...` lines elide repeats and uninteresting content per FR-006.
Combined non-blank line count: 25 (SC-007b ~25 budget).

Five recognizable Quadlet unit identifiers from
`examples/03-immich/services/` appear in the walkthrough
(immich-database, immich-server, traefik-edge, immich-internal,
immich-db-data) — well above SC-007's "≥ 1" threshold.

Notes from exercising the example:

- The `(stateless) (recovery from failed initial apply)` annotation
  in the second block reflects spec/017's stateless mode semantics:
  in stateless mode core-ops keeps no state under `/var/lib/`, so any
  plan invoked after a successful apply on the same host inspects
  raw host state and tags the run as "recovery from failed initial
  apply" because there is no persisted apply record. The annotation
  is truthful for stateless mode and is preserved verbatim.
- The host overlay drop-in
  `examples/03-immich/hosts/example/immich-ml/quadlet/immich-ml.container.d/20-gpu.conf`
  was removed locally on the recording host (no `/dev/dri`); this is
  the documented host-shape-mismatch workaround from
  `docs/onboarding-script.sh`.

README total: 295 lines (≤ 400). Verification:

$ cargo test                                                                  472 passed
$ cargo run --bin core-ops-release -- validate --base-ref master              passed (patch)
$ wc -l README.md                                                             295  (≤ 400)               PASS  SC-001
$ awk '/^## What using CoreOps feels like/,/^## Real-world examples/' README.md | grep -c '^```text'   2   PASS  FR-006
$ <walkthrough section>: 5 distinct unit identifiers from examples/03-immich/services/                  PASS  SC-007

T014 (record asciinema cast) and T016 (link cast from README) carry
through the recording-host work that follows.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… from README

T014: docs/onboarding.cast recorded against core-ops-uat (Fedora CoreOS
guest on ulthar libvirt) exercising plan → apply → idempotent re-plan
against `examples/03-immich --host example`. Asciicast v2 format
(SC-005), duration 4.92 s (well under the 90 s FR-007 cap), recorded at
30×110 with `--idle-time-limit=2`. Image cache was preserved across the
clean state wipe so the apply beat completed in ~4 s of network/start
activity rather than the ~5 min image-pull path.

T015: SC-006a stop-list grep returns zero matches across both the cast
and the regeneration script:

  grep -iE '(not\.one|ulthar|192\.168\.|10\.0\.|172\.16\.)' \
    docs/onboarding.cast docs/onboarding-script.sh    →   0 matches

The cast has been post-processed to strip pam_systemd-emitted OSC 3008
session-tracking sequences (which leak `hostname=core-ops-uat`,
`machineid=...`, and PID metadata when sudo creates a session under a
terminal that supports them) and to replace the recorder's
nix-store-path SHELL env value with `/bin/bash`. Both leak vectors are
outside the SC-006a regex but within FR-009a's broader sanitization
intent (no operator-private hostnames, no operator-private setup
values). The post-processor also computes the `duration` header field
from the last event timestamp — asciinema 2.4.0 omits it from the
header on completion in some cases, and SC-005a verifies via jq.

T016: README walkthrough section gains a one-line **Recording** anchor
linking `docs/onboarding.cast` with an `asciinema play` invocation hint.
No `<script>`, `<iframe>`, or `https://asciinema.org/...js` reference
in the diff (FR-013).

Process notes:

- The recording was produced via SSH delegation: asciinema 2.4.0 ran on
  the operator workstation (nix devshell), the inner `env -i bash`
  subshell ran `ssh -tq core@core-ops-uat 'cd ... && sudo <command>'`
  for each demo beat. This deviates from the canonical
  `docs/onboarding-script.sh` flow (which assumes a host with
  asciinema 2.4.0 + core-ops + repo all present locally) because
  Fedora CoreOS does not ship Python and rpm-ostree apply-live of
  python3-asciinema was not authorized on the shared UAT VM. The
  script remains valid for the documented use case.
- The OSC 3008 strip is a post-recording sanitization step. A future
  iteration could amend `docs/onboarding-script.sh` to apply it
  inline; flagged in synthesis follow-up.

Verification:

$ head -n 1 docs/onboarding.cast | jq '.version'                              2                          PASS  SC-005
$ head -n 1 docs/onboarding.cast | jq '.duration'                             4.918944  (≤ 90)            PASS  SC-005a
$ wc -l README.md                                                             297       (≤ 400)           PASS  SC-001
$ grep -E '(asciinema\.org/.*\.js|<iframe|<script)' README.md                 (no matches)               PASS  FR-013
$ grep -iE '(not\.one|ulthar|192\.168\.|10\.0\.|172\.16\.)' docs/onboarding.cast docs/onboarding-script.sh   (no matches)   PASS  SC-006a
$ asciinema play docs/onboarding.cast                                         plays end-to-end           PASS  SC-005
$ cargo run --bin core-ops-release -- validate --base-ref master              passed (patch)             PASS

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…r-FR/SC checks

Replaces the T001 skeleton table with 11 runnable checks (C-001 through
C-011), each labeled with the FR/SC anchor it implements. The checklist
is the falsifiable contract that catches future deviations from the
12-section FR-001 ordering, the FR-002 badge composition, the FR-009a
sanitization rule, and so on, without adding a CI lint step (FR-019
forbids the latter).

Corrections from authoring:

- C-002 (badge row composition) initially used `[![...](...)](...)`
  Markdown badge regex; the actual badges are `<a href><img></a>`
  HTML. Fixed to match HTML pattern + alt-text ordering.
- C-005 (walkthrough two-block + line budget) added a third sub-check
  for "≥ 1 recognizable Quadlet unit identifier from
  `examples/03-immich/services/`" — implements SC-007 directly rather
  than relying on visual inspection.
- C-006 (sanitization stop-list) is the canonical home of the SC-006a
  regex per `docs/onboarding-script.sh`'s header note. The OSC 3008
  caveat (pam_systemd-emitted hostname/machineid leak under sudo) is
  documented inline with a re-runnable Python post-processor for
  future re-recordings.
- C-010 (asciicast format and duration) verifies SC-005 and SC-005a
  via `jq '.version'` and `jq '.duration'`.
- C-011 (regeneration-script invariants) verifies the executable bit,
  shebang, and `examples/03-immich` literal occurrences (FR-009 / SC-006).

Self-test against post-018 tree:

  C-001  README ≤ 400  → 297                            PASS
  C-002  badge row     → 4 + correct alt order          PASS
  C-003  mental model  → line 20 (≤ 120)                PASS
  C-004  Mermaid       → 1 block, all substrings        PASS
  C-005  walkthrough   → 2 blocks, 25 non-blank, 5 unit IDs   PASS
  C-006  SC-006a       → 0 matches                      PASS
  C-007  SC-008        → 0 matches                      PASS
  C-008  link targets  → all resolve                    PASS
  C-009  FR-013        → 0 matches                      PASS
  C-010  cast format   → version=2, duration=4.918944  PASS
  C-011  script        → executable, shebang, 8x literal       PASS

Verification:

$ cargo test                                                                  472 passed
$ cargo run --bin core-ops-release -- validate --base-ref master              passed (patch)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…inal validate

Final pre-merge checks for spec/018. All pass against the post-018 tree
(commit 8f3ef28):

- T021 (structural): wc -l README.md = 297 (≤ 400, SC-001); 30-second
  mental-model heading present at line 20 (SC-003); Mermaid block
  present (SC-004).
- T022 (stop-list): 0 matches for hype-flagged terms (SC-008).
- T023 (link-resolve): every pre-018 link target (LICENSE, CHANGELOG.md,
  CODE_OF_CONDUCT.md, docs/development.md, examples/0[1-5]-*) resolves
  (SC-009).
- T024 (no-source-touched, FR-017 carve-out scope): `git diff
  master..HEAD --stat -- src/ .github/workflows/ examples/ LICENSE`
  returns empty. Cargo.toml/Cargo.lock and tests/ are excluded per
  the FR-017 carve-out (governance-driven version bump + mechanical
  fixture maintenance for FR-001 section renames).
- T027 (release-validate): Outcome=passed, Classification=releasable,
  Required=patch, Declared=patch, Version=patch, CHANGELOG=aligned.

Remaining tasks blocked on 24-hour cool-off:

- T025 (author self-attestation dogfooding pass) requires ≥ 24 h since
  the last README edit per Clarification Q4. README was last edited
  2026-05-07 ~06:30 UTC (T016 commit 08cd4a7); earliest T025 window
  opens 2026-05-08 ~06:30 UTC.
- T026 (author synthesis.md body) depends on T025's captured cold-read
  takeaway.
- T028 (PR ready-for-review) depends on T025/T026 landing.

These three tasks remain `[ ]` in tasks.md and will be picked up in a
follow-on session after the cool-off elapses.

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


Rebases onto post-promote master at v2.2.3 (which now contains both
spec/018-blocking fixes: PR #35 wired the immich-db-password Podman
secret into immich-server.container, and PR #36 introduced
ApplyRunDisplayState::Stateless so stateless re-plans no longer
flag a healthy host as "(recovery from failed initial apply)").

Re-recording on `core-ops-uat` (Fedora CoreOS guest) against the
fixed binary + fixed example produces the truthful narrative the
spec/018 walkthrough is supposed to demonstrate:

  Beat 1 (plan):   Plan for host example @ (stateless) (first run)
  Beat 2 (apply):  Apply for host example @ (stateless) (first run)
                   Outcome: converged
  Beat 3 (replan): Plan for host example @ (stateless)
                   10 unchanged

immich-server reaches `active running` (NRestarts=1) and journal
shows "Immich Microservices is running [v2.7.5] [production]" —
no auth restart loop. The misleading
"(recovery from failed initial apply)" suffix is gone.

Cast post-processed to strip OSC 3008 sequences (pam_systemd
hostname/machineid leak under sudo) and replace nix-store-path
SHELL env value, per decision_018-recording-ssh-delegation. Cast
duration 4.80 s, ≤ 90 s SC-005a budget.

T013 README walkthrough block updated to keep verbatim fidelity to
the new T012 capture: the second fenced block's header and underline
now match the corrected output (no "(recovery)" suffix; underline
length adjusted from 72 chars to 35 chars to match the shorter title).
Block count = 2 (FR-006), combined non-blank lines = 25 (SC-007b),
unique unit identifiers = 5 (SC-007).

The rebase dropped the prior `chore(release): bump 2.2.1 -> 2.2.2`
commit (subsumed by master's PR #34/#35/#36 promotes); spec/018 now
bumps `2.2.3 -> 2.2.4` per `packaged_readme_surface` carve-out.

Verification:

$ cargo test                                                                  473 passed
$ cargo clippy --all-targets -- -D warnings                                   clean
$ cargo run --bin core-ops-release -- validate --base-ref master              passed (patch)
$ wc -l README.md                                                             297       (≤ 400)            PASS  SC-001
$ <walkthrough section>: 2 fenced blocks, 25 non-blank lines, 5 unit IDs       PASS  FR-006/SC-007/SC-007b
$ head -n 1 docs/onboarding.cast | jq '.version'                              2                            PASS  SC-005
$ head -n 1 docs/onboarding.cast | jq '.duration'                             4.80      (≤ 90)             PASS  SC-005a
$ grep -iE '(not\.one|ulthar|192\.168\.|10\.0\.|172\.16\.)' docs/onboarding.cast docs/onboarding-script.sh
                                                                              (no matches)                 PASS  SC-006a
$ grep -c '3008' docs/onboarding.cast                                         0                            PASS  FR-009a
$ asciinema play docs/onboarding.cast                                         plays end-to-end             PASS

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

The pre-existing "play locally with asciinema play" footnote had effective
reach near zero — most operators encounter the project via GitHub and
will not clone the repo + install asciinema to view a recording. The
spec's research.md F1 follow-up explicitly named this gap and proposed
an inline-renderable sidecar; this change promotes F1 from "follow-up"
to in-scope for spec/018 and chooses the highest-friction-removed path:
an animated GIF rendered via `agg` (asciinema-agg) from the same `.cast`
source, embedded inline in the README via standard HTML image syntax
wrapped in an asciinema.org anchor.

Replaces the prior single-line "Recording: [...]" footnote with the
canonical centered-figure pattern (HTML, no JS):

  <p align="center">
    <a href="https://asciinema.org/a/CAST_ID">
      <img src="docs/assets/core-ops-demo.gif" alt="..." width="820">
    </a>
  </p>
  <p align="center">
    <a href="...">Watch the full terminal session on asciinema</a>
  </p>

The GIF (101 KB, GIF89a, well under SC-005b's 1 MB soft cap) plays inline
on GitHub on first paint with no operator action; the click-through goes
to asciinema.org's full hi-fi player for readers who want pixel-perfect
playback or to copy commands.

Spec amendments (FR-007 / FR-009 / FR-013 / new SC-005b):

- FR-007 expanded: `.cast` remains source-of-truth; `docs/assets/core-ops-demo.gif`
  is a derived sidecar produced by `agg` from the same source, regenerated
  whenever the cast is re-recorded.
- FR-009 expanded: `docs/onboarding-script.sh` now invokes `agg` after
  asciinema and pins both versions in its header (asciinema 2.4.0,
  agg 1.7.0).
- FR-013 clarified: the prohibition on third-party JS embeds stays;
  standard Markdown / HTML image embeds of the in-tree GIF are
  explicitly permitted (no JS, renders everywhere Markdown renders).
  The asciinema.org SVG-badge embed pattern is also permitted (static
  SVG, not a script).
- SC-005b new: GIF file MUST exist, MUST be a real GIF (`GIF87a`/
  `GIF89a` magic), README MUST embed it via image-tag syntax, soft
  size cap ≤ 1 MB.
- SC-006a clarified: the same stop-list applies indirectly to the GIF
  via the `.cast` source — anything not in the cast cannot appear in
  the rendered frames.

Tooling and regeneration:

- `flake.nix`: adds `asciinema-agg` (binary `agg`, version 1.7.0) to
  the dev shell. Note: `pkgs.agg` is the unrelated Anti-Grain Geometry
  C++ library; don't confuse the two.
- `docs/onboarding-script.sh`: pins agg version, fails fast if agg is
  not on PATH, runs `agg --idle-time-limit 2 --quiet $CAST $GIF` after
  recording, prints the rendered file's size as a soft-cap proxy.
- `checklists/readme-structure.md` C-011a: new runnable check verifying
  GIF existence, magic bytes, size, and README embed.

Operator follow-up: the README anchors `https://asciinema.org/a/CAST_ID`
with `CAST_ID` as a literal placeholder. Once the cast is uploaded to
asciinema.org under the maintainer account (`asciinema auth` then
`asciinema upload docs/onboarding.cast`), the operator substitutes the
real cast ID via a one-line follow-up commit. The local GIF embed
works inline without the substitution; only the click-through anchor
is broken until the upload happens.

Verification:

$ test -f docs/assets/core-ops-demo.gif                                       0     PASS  SC-005b
$ head -c 6 docs/assets/core-ops-demo.gif                                     GIF89a PASS  SC-005b
$ wc -c < docs/assets/core-ops-demo.gif                                       101278 (≤ 1MB)  PASS  SC-005b
$ grep -E 'docs/assets/core-ops-demo\.gif' README.md | wc -l                  1     PASS  SC-005b
$ grep -E '(asciinema\.org/.*\.js|<iframe|<script)' README.md | wc -l         0     PASS  FR-013
$ wc -l README.md                                                             305   (≤ 400)   PASS  SC-001
$ cargo test                                                                  473 passed
$ cargo clippy --all-targets -- -D warnings                                   clean
$ cargo run --bin core-ops-release -- validate --base-ref master              passed (patch)

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

Operator reported staircase distortion in the rendered cast/GIF: each
successive line started indented by the length of the previous line,
the classic "LF without CR" pattern.

Root cause: the inner recording script piped SSH output through
`| sed 's/\r$//'`, which stripped every `\r` that the remote PTY's
ONLCR translation had added. The captured cast events therefore
contained bare `\n` (line feed only). asciinema 2.4.0 plays casts
through a raw-mode local TTY (so it can faithfully replay any
escape sequences without double-translation), which means LF stays
LF on output — the terminal treats each LF as "down one row" with
no column reset, hence the staircase.

The original sed filter was probably defensive — line-buffered tools
sometimes choke on trailing `\r` — but for asciinema-capture the CRs
are essential. Drop the filter and re-record.

After re-recording the plan-output event has 124 CRs and 124 LFs
(matched), confirming `\r\n` line terminators throughout. `asciinema
cat docs/onboarding.cast` now renders left-aligned. The GIF rendered
from this cast (`docs/assets/core-ops-demo.gif`, 107 KB GIF89a)
displays the correct terminal layout inline on GitHub.

Cast re-recorded against the same fixed stack (PRs #34/#35/#36
merged):

  Beat 1 (plan):   Plan for host example @ (stateless) (first run)
  Beat 2 (apply):  Apply for host example @ (stateless) (first run)
                   Outcome: converged
  Beat 3 (replan): Plan for host example @ (stateless)
                   10 unchanged

Same OSC 3008 strip + SHELL sanitize post-processing applied (per
decision_018-recording-ssh-delegation). Duration 4.88 s, well under
SC-005a's 90 s budget.

Verification:

$ head -n 1 docs/onboarding.cast | jq '.version'                  2
$ head -n 1 docs/onboarding.cast | jq '.duration'                 4.88012  (≤ 90)
$ grep -c '3008' docs/onboarding.cast                             0
$ grep -iE '(not\.one|ulthar|192\.168\.|10\.0\.|172\.16\.)' docs/onboarding.cast docs/onboarding-script.sh
                                                                  (no matches)
$ asciinema cat docs/onboarding.cast | head -3                    left-aligned, no staircase
$ wc -c < docs/assets/core-ops-demo.gif                           106777  (≤ 1 MB)

The non-tracked recording driver `/tmp/onboarding-inner.sh` carried
the buggy sed pipe in this session; the canonical
`docs/onboarding-script.sh` does NOT have a sed filter (its inner
`demo()` runs commands directly via `eval`, not through SSH
delegation). The bug therefore lives only in the SSH-delegation
recording procedure documented in
decision_018-recording-ssh-delegation, not in the canonical
regeneration script.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #37 (refactor(apply): unify init'd and stateless dispatch behind
ApplyTarget) landed on master and the post-merge promote tagged
v2.2.4. The fix delivers the spec/006 streaming output to stateless
mode, which is the prerequisite spec/018's recording was waiting on.

Bump 2.2.4 -> 2.2.5 (`packaged_readme_surface` carve-out;
provenance-state fixture pinned in lock-step).

FR-007 amendments:

- The recording is now **apply-only**. The static plan-output and
  idempotent-re-run blocks in the README walkthrough section (per
  FR-006) are the canonical source of plan/re-plan content;
  including those beats in the recording adds duration without
  motion. The 90s budget is preserved for the apply step where
  the line-by-line streaming output now matters.
- The recording MUST be produced on a host where `core-ops` runs
  natively (not via SSH delegation). SSH transport (even with
  `-t` PTY allocation) collapses the streaming timing that the
  recording is meant to capture; SSH-delegated recording is
  explicitly out of spec going forward. The decision file
  decision_018-recording-ssh-delegation captures the prior
  procedure as a session-3 historical artifact, not a forward
  recipe.

Tooling amendment:

- `docs/onboarding-script.sh` now records a single beat (apply
  only) instead of the prior plan -> apply -> re-plan trio. The
  command sequence in the BASH heredoc is one `demo 'sudo
  core-ops apply --source-repo examples/03-immich --host
  example'` invocation. Header rewritten to match.

The actual cast + GIF re-recording is the operator's next step,
to be done natively on a host with `core-ops` 2.2.5 + asciinema
2.4.0 + agg 1.7.0 + the repo present (or an equivalent on-host
setup). The current `docs/onboarding.cast` and
`docs/assets/core-ops-demo.gif` remain in tree from the prior
SSH-delegated session-3 work and will be replaced once the
operator records natively.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@outergod outergod force-pushed the 018-adoption-readiness branch from c37fe6e to 4c37637 Compare May 7, 2026 10:46
outergod and others added 5 commits May 7, 2026 13:09
…s v3

Operator re-recorded `docs/onboarding.cast` natively against the
PR #37 fix (core-ops 2.2.5 with stateless streaming). Duration
4.97 s. The recording captures the line-by-line "creating... →
created" progression that asciinema 3.x's PTY observation preserves
end-to-end — exactly the UX FR-007 was rewritten to require, and the
SSH-delegation procedure was unable to capture.

Cast was produced by asciinema 3.x, which writes asciicast v3 by
default. Spec amendments to accommodate:

- FR-008: now accepts asciicast v2 OR v3. Both are rendered by `agg`
  1.7.0 and played by asciinema.org. The header MUST declare
  `"version": 2` or `"version": 3`.
- SC-005: same — version MUST be 2 or 3.
- SC-005a: duration calculation now branches on version. v2 has an
  absolute `duration` header field; v3 events are `[delta, type, data]`
  triples and total duration = sum of all deltas. Either MUST be ≤ 90.
- Checklist C-010 updated with the version-branching one-liner.

Post-recording sanitization (per decision_018-recording-ssh-delegation
and checklist C-006):

- OSC 3008 sequences stripped from event data (pam_systemd-emitted
  `hostname=core-ops-uat`, `machineid=...`, PID metadata under sudo).
  The post-processor is v3-aware (event shape is identical to v2;
  header schema differs but the SHELL/duration handling is unchanged
  for our purposes).
- nix-store-path SHELL env value cleaned to `/bin/bash`.

GIF re-rendered from the sanitized cast: 56 KB GIF89a, well under
SC-005b's 1 MB soft cap.

Verification:

$ head -1 docs/onboarding.cast | jq '.version'                                3
$ awk 'NR>1' docs/onboarding.cast | jq -s 'map(.[0]) | add'                  4.965  (≤ 90)  PASS  SC-005a
$ grep -c '3008' docs/onboarding.cast                                         0          PASS  FR-009a
$ grep -iE '(not\.one|ulthar|192\.168\.|10\.0\.|172\.16\.)' docs/onboarding.cast docs/onboarding-script.sh
                                                                              (no matches) PASS  SC-006a
$ head -c 6 docs/assets/core-ops-demo.gif                                     GIF89a       PASS  SC-005b
$ wc -c < docs/assets/core-ops-demo.gif                                       56923  (≤ 1MB) PASS  SC-005b
$ cargo run --bin core-ops-release -- validate --base-ref master              passed (patch)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two operator-driven changes wrapped together:

- Substitute the CAST_ID placeholder with the asciinema.org-uploaded
  cast (https://asciinema.org/a/XKhpmzM8ZWg56Wu2). Both the
  GIF-anchor `<a href>` and the secondary "Watch the full terminal
  session on asciinema" link resolve. Visitors get inline motion via
  the GIF on first paint and an optional click-through to the
  hi-fi asciinema.org player.
- Operator-driven section reorder of README.md (no FR-001 ordering
  change required by spec — operator's reordering reflects how the
  walkthrough flows when reading top-to-bottom).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Operator manually re-recorded `docs/onboarding.cast` with the
paginated plan beat folded in alongside the apply step (the kind
of UX a future built-in pager would deliver — see follow-up
discussion). Duration 13.36 s, comfortably inside SC-005a's 90 s
budget.

Cast ID on asciinema.org rotated XKhpmzM8ZWg56Wu2 -> HaqIw05gehGk2YpH
(both `<a href>` anchors in the README walkthrough section now point
at the new upload).

Same post-recording sanitization applied as the prior cast (per
checklist C-006 and decision_018-recording-ssh-delegation):

- 4 OSC 3008 session-tracking sequences stripped from event data
  (pam_systemd-emitted `hostname=core-ops-uat`, `machineid=...`,
  PID metadata under sudo).
- SHELL env scrubbed of nix-store path.

GIF re-rendered from the sanitized cast: 755 KB GIF89a 1315×918,
under SC-005b's 1 MB soft cap.

Verification:

$ head -1 docs/onboarding.cast | jq '.version'                                3
$ awk 'NR>1' docs/onboarding.cast | jq -s 'map(.[0]) | add'                  13.358  (≤ 90)  PASS  SC-005a
$ grep -c '3008' docs/onboarding.cast                                         0          PASS  FR-009a
$ grep -iE '(not\.one|ulthar|192\.168\.|10\.0\.|172\.16\.)' docs/onboarding.cast docs/onboarding-script.sh
                                                                              (no matches) PASS  SC-006a
$ head -c 6 docs/assets/core-ops-demo.gif                                     GIF89a       PASS  SC-005b
$ wc -c < docs/assets/core-ops-demo.gif                                       754920 (≤ 1MB) PASS  SC-005b
$ grep -c 'asciinema.org/a/HaqIw05gehGk2YpH' README.md                        2          PASS  cast linked
$ cargo run --bin core-ops-release -- validate --base-ref master              passed (patch)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
External cold readers (non-CoreOps audience) found the prior structure
hard to parse. Operator authored a leaner, plain-language pass
(305 -> 211 lines) ahead of a second cold-read round. Captures friend
feedback as the effective T025 dogfooding signal; synthesis.md entry
and any FR/SC amendments follow in subsequent commits.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Operator-side dev tooling for spec/018-adjacent JS/web work. Not
consumed by the Rust build; devshell-only.

Co-Authored-By: Claude Opus 4.7 (1M context) <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.

1 participant