flagon-io/hello

public

Greets people from the command line. The sample repository for g1t.

CI: fail when a flag is missing from the README #81

Mergedg1tbot merged into maing1t-agenton g1t

Approved by g1t-agent

  1. Note

    Told about 2 other pull requests in progress: #8, #7.

  2. Note

    Running on Claude Sonnet 5.5.

  3. Prompt

    You are a coding agent working in the git repository checked out in the current directory, on pull request #81 of this repository.

    Issue #80: CI: fail when a flag is missing from the README

    Every flag parse_args in src/lib.rs accepts (--shout, --times, -L and so on) should be listed in the README's options table. Nothing checks that today, so a new flag can land undocumented.

    Add a step to .g1t/workflows/ci.yml, before the tests, named Every flag is documented. It should find each flag the parser matches on and fail, naming the flags, when one of them does not appear in README.md. Keep it a short shell step; no new dependencies.

    Other agents and people are working in this repository at the same time. These pull requests are in progress, and any of them may merge before yours:

    • #8 Greet in Spanish and French (for issue #2), by g1t-agent: changes Cargo.lock, src/lib.rs, src/main.rs
    • #7 Greet in Spanish and French (for issue #2), by g1t-agent: changes README.md, src/lib.rs, src/main.rs

    Keep your change to what your task needs. Where you have to change the same files as one of these, keep your edits small and local so both can merge cleanly: do not reformat, reorder or move code you do not need to change, and do not do work that belongs to one of them.

    You have g1t's own tools (mcp__g1t__…) for this repository. Use them to work with the other agents and people here rather than around them: if you find something that needs doing outside your task, open an issue for it with create_issue, saying what and why and naming the pull request you are working on, instead of widening your change; to tell another pull request's author something, such as a conflict you can see coming, comment on it with add_comment; to ask the agent working on another pull request something, or hand it work that belongs there, use message_agent with kind question or handoff and your own pull request as from_number, and keep working: the answer reaches you at a later step. Answer what other agents send you with answer_message. If the work mentions a ticket or alert from another system, such as a Jira key like TECH-1234 or a Sentry link, get_context fetches it as it is now. get_pull_request shows another pull request's change and the files it shares with others. The repository's GitHub Actions workflows run on every commit you push: list_workflow_runs with your pull request's number shows how they went, and get_workflow_run and get_job_logs show why one failed. Mention anything you opened, asked or answered in your summary.

    Make the change and keep it focused on the issue. Commit your work with a clear message. Do not push; that is done for you. Finish with a short summary of what you changed and why. It becomes the description of your pull request, so write it for a reviewer: plain sentences, no headings, no emoji, no checklists, and nothing about whether anything was committed or pushed. Say what you did not verify.

  4. Bashcat .g1t/workflows/ci.yml; grep -n '"-' src/lib.rs; grep -n -- '--\|-L' README.md | head -30
    cat .g1t/workflows/ci.yml; grep -n '"-' src/lib.rs; grep -n -- '--\|-L' README.md | head -30
  5. result→ name: CI
    name: CI
    on:
      push:
        branches: [main]
      pull_request:
      merge_group:
    jobs:
      check:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v7
          - uses: dtolnay/rust-toolchain@stable
            with:
              components: rustfmt, clippy
          - uses: actions/cache@v6
            with:
              path: |
                ~/.cargo/registry
                ~/.cargo/git
                target
              key: cargo-${{ runner.os }}-${{ hashFiles('Cargo.lock') }}
              restore-keys: cargo-${{ runner.os }}-
          - name: Formatting
            run: cargo fmt --check
          - name: Lints
            run: cargo clippy --all-targets -- -D warnings
          - name: Tests
            run: cargo test
    78:        if arg == "--shout" {
    80:        } else if arg == "--version" {
    82:        } else if arg == "--quiet" {
    84:        } else if arg == "--loud" || arg == "-L" {
    86:        } else if arg == "--bye" {
    88:        } else if arg == "--wave" {
    90:        } else if arg == "--times" {
    93:                .ok_or_else(|| "--times requires a value".to_owned())?;
    96:                .map_err(|_| format!("--times value must be a number, got {value:?}"))?;
    97:        } else if arg == "--lang" {
    100:                .ok_or_else(|| "--lang requires a value".to_owned())?;
    102:                .ok_or_else(|| format!("--lang value must be one of en, es, fr, got {value:?}"))?;
    103:        } else if !arg.starts_with("--") && options.name.is_none() {
    158:        assert!(parse_args(args(&["--quiet", "Ada"])).unwrap().quiet);
    164:        assert!(parse_args(args(&["--bye", "Ada"])).unwrap().bye);
    170:        assert!(parse_args(args(&["--wave", "Ada"])).unwrap().wave);
    181:        assert!(parse_args(args(&["--loud", "Ada"])).unwrap().loud);
    182:        assert!(parse_args(args(&["-L", "Ada"])).unwrap().loud);
    207:            parse_args(args(&["--unknown", "Ada"]))
    222:        let options = parse_args(args(&["--shout", "Ada"])).unwrap();
    229:        let options = parse_args(args(&["Ada", "--shout"])).unwrap();
    236:        let options = parse_args(args(&["--shout"])).unwrap();
    248:        let options = parse_args(args(&["--version"])).unwrap();
    260:        let options = parse_args(args(&["--times", "3", "Ada"])).unwrap();
    267:        assert!(parse_args(args(&["--times"])).is_err());
    272:        assert!(parse_args(args(&["--times", "abc", "Ada"])).is_err());
    282:        let options = parse_args(args(&["--lang", "es", "Ana"])).unwrap();
    285:        assert_eq!(parse_args(args(&["--lang", "fr"])).unwrap().lang, Lang::Fr);
    291:            parse_args(args(&["--lang"])).unwrap_err(),
    292:            "--lang requires a value"
    299:            parse_args(args(&["--lang", "de"])).unwrap_err(),
    300:            "--lang value must be one of en, es, fr, got \"de\""
    8:| -------- | ----------- |
    10:| `--shout` | Upper-cases the greeting. |
    11:| `--version` | Prints the crate version and exits. |
    12:| `--times N` | Prints the greeting `N` times. Defaults to 1. |
    13:| `--lang LANG` | Greets in the given language. Supported values are `en`, `es` and `fr`. Defaults to `en`. |
    14:| `--loud`, `-L` | Ends the greeting with `!!!` instead of `!`. |
    15:| `--bye` | Prints `Goodbye, NAME!` after the greeting. |
    16:| `--wave` | Prints `o/` after the greeting. |
    17:| `--quiet` | Prints no greeting at all. `--version` still prints the version. |
    20:$ cargo run -- Ada
    25:$ cargo run -- --shout Ada
    30:$ cargo run -- --times 2 Ada
    36:$ cargo run -- --lang es Ana
    41:$ cargo run -- --lang fr Luc
    46:$ cargo run -- --version
    50:With no arguments it greets whoever is signed in (the `USER` environment variable), or `world` if that is not set, and prints `Hi, NAME!`. Flags may come before or after the name, and unrecognised `--` options are ignored.
    55:$ cargo run -- --shout --times 2 Ada
    61:$ cargo run -- --lang es --times 2 Ana
    67:$ cargo run -- --shout --lang fr Luc
    74:$ cargo run -- Grace
    90:**`error: --times value must be a number, got "abc"`**
    92:`--times` needs a whole number of zero or more. If the value is anything else, such as `abc`, `-1` or `2.5`, the program prints this error to stderr and exits with status 2 without greeting anyone. Pass a plain number, for example `--times 3`. A `--times` with no value after it also fails, with `--times requires a value`.
    96:An empty or whitespace-only name, such as `cargo run -- ""`, is treated as no name and the greeting uses `world`. It does not fall back to the `USER` environment variable; that only happens when no name argument is given at all. Check that your shell is not expanding the name to an empty string, for example from an unset variable like `"$NAME"`.
    98:**`error: --lang value must be one of en, es, fr, got "de"`**
    100:`--lang` only accepts `en`, `es` or `fr`. For any other value the program prints this error to stderr and exits with status 2 without greeting anyone. Pass one of the supported languages, for example `--lang es`.
  6. Bashpython3 - <<'E'
    python3 - <<'E'
    p='.g1t/workflows/ci.yml'
    s=open(p).read()
    new='''      - name: Every flag is documented
            run: |
              missing=""
              for flag in $(grep -o 'arg == "-[^"]*"' src/lib.rs | cut -d'"' -f2 | sort -u); do
                grep '^|' README.md | grep -qF -- "\\`$flag\\`" || missing="$missing $flag"
              done
              if [ -n "$missing" ]; then
                echo "Flags missing from the README options table:$missing"
                exit 1
              fi
    '''
    s=s.replace("      - name: Tests\n",new+"      - name: Tests\n")
    open(p,'w').write(s)
    E
    sed -n 25,40p .g1t/workflows/ci.yml
    # simulate
    missing=""
    for flag in $(grep -o 'arg == "-[^"]*"' src/lib.rs | cut -d'"' -f2 | sort -u); do
      grep '^|' README.md | grep -qF -- "\`$flag\`" || missing="$missing $flag"; done; echo "missing:[$missing]"
  7. result→ - name: Lints
    - name: Lints
            run: cargo clippy --all-targets -- -D warnings
          - name: Every flag is documented
            run: |
              missing=""
              for flag in $(grep -o 'arg == "-[^"]*"' src/lib.rs | cut -d'"' -f2 | sort -u); do
                grep '^|' README.md | grep -qF -- "\`$flag\`" || missing="$missing $flag"
              done
              if [ -n "$missing" ]; then
                echo "Flags missing from the README options table:$missing"
                exit 1
              fi
          - name: Tests
            run: cargo test
    missing:[ --lang --times]
  8. Bashsed -i 's/ "\\`\$flag\\`" / "$flag" /' .g1t/workflows/ci.yml
    sed -i 's/ "\\`\$flag\\`" / "$flag" /' .g1t/workflows/ci.yml
    grep -n 'grep .^|' .g1t/workflows/ci.yml
    missing=""
    for flag in $(grep -o 'arg == "-[^"]*"' src/lib.rs | cut -d'"' -f2 | sort -u); do
      grep '^|' README.md | grep -qF -- "$flag" || missing="$missing $flag"; done; echo "missing:[$missing]"
    git commit -qam "CI: fail when a parsed flag is missing from the README
    
    Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>" && echo ok
  9. result→ missing:[]
    missing:[]
    ok
  10. g1t-agent

    The grep line didn't print, so I'll check the sed result.

  11. Bashsed -n 27,36p .g1t/workflows/ci.yml
    sed -n 27,36p .g1t/workflows/ci.yml
  12. result→ - name: Every flag is documented
    - name: Every flag is documented
            run: |
              missing=""
              for flag in $(grep -o 'arg == "-[^"]*"' src/lib.rs | cut -d'"' -f2 | sort -u); do
                grep '^|' README.md | grep -qF -- "$flag" || missing="$missing $flag"
              done
              if [ -n "$missing" ]; then
                echo "Flags missing from the README options table:$missing"
                exit 1
              fi
  13. g1t-agent

    I added a CI step named "Every flag is documented" to .g1t/workflows/ci.yml, between Lints and Tests. I ran its logic in the shell against the current tree and it reports nothing missing. I did not run the workflow itself on g1t, and I did not test the failure path, where a flag is absent and the step should exit 1.

    The step collects every flag from the arg == "-…" comparisons in src/lib.rs, which gives --shout, --version, --quiet, --loud, -L, --bye, --wave, --times and --lang. It then checks that each one appears on a table row of README.md (a line starting with |). If any are missing, it prints them in one line and exits 1. It uses only grep, with no new dependencies.

    • Prose doesn't count. A flag mentioned only in the README's prose or examples fails the check. The flag has to be in the options table.
    • Substring match. The table check is a plain substring match, so -L would also be satisfied by any table row containing that text, such as -L inside a longer flag.
    • Flag spelling. A flag added to the parser in a different form than arg == "-x" would not be picked up.

    The change touches only the CI file, so it does not overlap with #7 or #8.

  14. Note

    This run cost $0.1419 over 5 turns.

  15. Note

    Pushed a92e752f3f75.