Skip to content

Epic 16 · The contract says only what the code does #456

Closed
6 of 6 issues completed
Closed

Description

@pascalandy

The problem (CMO)

The 2026-11-03 reality check found six places where nicely promises more than its code does:

  • A YouTube sign-in request gets TEMPORARY (exit 75), so an agent retries a refusal that waiting does not fix. This occurred twice on the mbp on 2026-10-20.
  • Each transcript call without --run-id that fails the output or key check leaves one more lock file. nicely never deletes lock files.
  • just update runs git, uv, and Copier with the GIT_* variables of its caller. Under a git hook, git can write in the wrong repository.
  • ncly with a scratch HOME still reaches the real Secret Service on Linux. A test auth login can write to the user's keyring.
  • An extension pinned to a release reads the documentation of main through 47 blob/main links. The template also names a private skill.
  • The principles promise the OS trash, debug details from core, and help that names the wrapped tool. No code does these things.

REF: #295

The outcome (FMO)

Each answer, check, and promise above matches the code. Each fix has a test or a check that fails on the old behavior.

Stories

  1. A YouTube sign-in request answers TEMPORARY. (fix(transcript): answer a YouTube sign-in request with its cause, not TEMPORARY #295) · code + tests
    • Today: youtube.py searches the whole diagnostic for a 429, so a 429 warning wins over the final sign-in error.
    • After: the final ERROR record decides the code. A sign-in request answers TRANSCRIPT_DOWNLOAD_FAILED (exit 1), and its hint names the browser setting.
  2. A refused transcript call leaves a lock file. (fix(transcript): a call without a run ID that fails its checks leaves no lock file #443) · code + tests
    • Today: extensions/transcript/execute.go line 34 opens the run before the output and key checks of lines 59-67.
    • After: a call without --run-id checks the output and the key first. Such a refusal adds no file to the runs folder.
  3. just update gives its programs the variables of its caller. (fix(sdk): just update runs its programs without the GIT_* variables of its caller #444) · code + tests
    • Today: updateRun and updateOutput in sdk/extensionrepo/update.go start programs with no Env. callerenv checks three exact package paths only.
    • After: each program of just update gets an explicit environment without GIT_*. callerenv checks each package under the paths that it owns.
  4. ncly with a scratch HOME reaches the real keyring. (test(auth): on Linux, tests and verify-nicely use a private keyring #445) · code + tests
    • Today: the keyring library finds the Secret Service through the D-Bus session of the user, not through HOME. The auth scenarios use a fake keychain.
    • After: on Linux, a test runs the real ncly against a private keyring, and verify-nicely uses a private keyring.
  5. A pinned extension reads the documentation of main. (fix(docs): links point to the release tag, and no private skill is named #446) · code + tests
    • Today: the template has 44 blob/main links, the bundled nicely-extensions skill has 3, and replace-a-file.md line 52 names a private skill.
    • After: each link points to the release tag of the template or of the running ncly. The template names no private skill.
  6. The principles promise features with no code. (docs(north-star): promise the trash, core debug details, and tool credit only when a command needs them #447) · text
    • Today: principle 4 promises the OS trash, global-flags.md promises debug and verbose details from core, and principle 10 promises help that names the tool.
    • After: each text says that the feature comes when a command needs it, through the SDK. Each rule links the item or decision that delivers it.

Done when

The scenarios of #295, #443, #444, and #446 pass in just check. The private keyring test of #445 passes on om1. The texts of #447 promise only what the code does.
Every Story has a documented disposition. The outcome has delivery evidence.

👨🏻‍🍳 For the agent

👨🏻‍🍳 Details

Out of this Epic

File conflicts

None between these six Stories. #446 and #451 both change template/AGENTS/rule-index/loop.md.jinja, so #451 waits for #446.

Uncertainty

Approaches considered

  • An SSA analyzer for a command with no Env: refused. An explicit Env with a poisoned test proves more for less.
  • A sweeper that deletes old lock files: refused. Deletion of a flock file races with a new lock.
  • The Python API of yt-dlp: refused. Its errors still give text, not stable codes.
  • Code for the trash, core debug details, and tool credit now: refused (Pascal, 2026-11-03, 2a). No command needs them yet.

by Opus-5.5 via Claude Code

Activity

  1. added
    3-pty:p2Normal planned work; default when no priority is established
    4-epicGroups Stories around a shared outcome through native sub-issues
    on Nov 3, 2026
  2. added
    1-ready-for-agentValidated work, sufficiently specified for an autonomous agent
    on Nov 3, 2026
  3. pascalandy commented on Nov 4, 2026

    @pascalandy
    OwnerAuthor

    Done. All six Stories merged to main at 531b3b4 on 2026-11-04 and shipped in v0.26.1.

    Story PR
    #447 #461
    #446 #463
    #295 #464
    #443 #465
    #444 #469
    #445 #470

    Done when:

    The same stack also merged #462 (#466) and #468 (#471), which were outside the plan, and #450 (#472) of Epic #458. Follow-ups: #473, #474, #475, #476, #477.

    by Opus-5.5 via Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    1-ready-for-agentValidated work, sufficiently specified for an autonomous agent3-pty:p2Normal planned work; default when no priority is established4-epicGroups Stories around a shared outcome through native sub-issues

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions