Skip to content

fix(team): name the App before team init converges, not after (#469) - #488

Merged
shadowbrush merged 2 commits into
mainfrom
fix/team-init-app-scope
Aug 18, 2026
Merged

fix(team): name the App before team init converges, not after (#469)#488
shadowbrush merged 2 commits into
mainfrom
fix/team-init-app-scope

Conversation

@shadowbrush

Copy link
Copy Markdown
Member

Closes #469. Last of the #458 ambient-scope family — and the mild one, scoped that way when I filed it and still that way now.

The gap

team init resolved its App through the same silent chain as the rest of the group. Its receipt already half-answered the question:

✓ worklog collection(s) created in hrn:mem:acme.com:eng-team

That URN carries the org and team slug, so a reader who looks can tell whose memory it hit. Two gaps remained, and only one is really about scope:

  1. It arrives after the converge. team init writes; the one moment a reader could catch a wrong ambient App is the moment that has already passed.
  2. It never said which of the four branches picked the App.

Both closed by one line, before the converge:

app: hrn:app:acme.com:eng-team — Eng Team (from --app)
✓ worklog collection(s) unchanged in hrn:mem:acme.com:eng-team

Why a line, not the chat post pre-flight

#470 got a stderr pre-flight because a post cannot be recalled and its mentions fire on write. The converge is idempotent and non-destructive, so this is orientation, not a safety signal — and it belongs on stdout with the rest of the human render.

There is a precedent in this same file for the weight: the -m override already emits a stderr note when the named memory is not the App's team memory, because a silently-redirected write is confusing. This is the ambient path finally getting its equivalent.

Deliberately unchanged

  • The receipt. It answers a different question — which memory the collections landed in, which can differ from the App's own shared memory — and the -m mismatch note already covers that divergence.
  • --json. Shape unchanged, and it issues no identity read.
  • The -m path prints no source line. It resolves its App from the named memory via appForTeamMemory, not from the ambient chain, so a "from …" phrase there would answer a question nobody asked.

One implementation note worth a reviewer's eye: the line is explicitly conditioned on --json rather than living inside an output.Write human branch, because it has to print before the operation the receipt describes. Same contract either way, and the test pins both halves — ordering on the human path, and no leakage or extra call under --json.

Tests

TestTeamInitNamesTheAppBeforeConverging asserts the scope line leads the output, that the existing receipt survives, that --json stays parseable (i.e. the line never leaks into stdout) with every documented key, and that no identity read is issued there.

Mutation-checked: removed the line and watched it go red on ordering.

make test + make lint green.

The family

#465 worker · #471 role · #475 chat · #478 the worktree guardrail · this — complete.

— Jonas (cli-engineer) · hrn:worker:hadronmemory.com:hadron-dev-team:jonas

🤖 Generated with Claude Code

Last of the #458 ambient-scope family, and the mild one — deliberately
scoped that way when I filed it, and it stayed that way.

`team init` resolved its App through the same silent chain as the rest of the
group. Its receipt already half-answered the question:

  ✓ worklog collection(s) created in hrn:mem:acme.com:eng-team

That URN carries the org and team slug, so a reader who looks CAN tell whose
memory it hit. Two gaps remained, and only one of them is really about
scope:

1. It arrives AFTER the converge. `team init` writes; the one moment a reader
   could catch a wrong ambient App is the moment that has already passed.
2. It never said which of the four branches picked the App.

Both closed by one line, printed before the converge:

  app: hrn:app:acme.com:eng-team — Eng Team (from --app)

Why a line and not the stderr pre-flight `chat post` got in #470: the converge
is idempotent and non-destructive, so this is orientation, not a safety
signal on an unrecallable write. There is a precedent in this same file for
the weight — the -m override already emits a stderr note when the named
memory is not the App's team memory (init.go), because a silently-redirected
write is confusing. This is the ambient path finally getting its equivalent.

The existing receipt is untouched. It answers a different question — which
MEMORY the collections landed in, which can differ from the App's own shared
memory — and the -m mismatch note already covers that divergence.

Scoped out, as filed: --json unchanged and issues no identity read; the -m
path prints no source line, because it resolves its App from the named memory
via appForTeamMemory rather than from the ambient chain, so a "from …" phrase
there would answer a question nobody asked.

The guard is explicitly conditioned on --json rather than living in an
output.Write human branch, because it has to print before the operation the
receipt describes. Same contract either way, and the test pins both halves.

Test mutation-checked: removed the line and watched it go red on ordering.

Worker: Jonas (cli-engineer) <hrn:worker:hadronmemory.com:hadron-dev-team:jonas>

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 25556ae5ad

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/cmd/team/init.go Outdated
// memory (appForTeamMemory), not from the ambient chain, so a
// "from …" phrase there would be answering a question nobody asked.
if !f.JSON {
fmt.Fprintf(f.IOStreams.Out, "app: %s\n", lazyAppLabel(ctx, f, scope)())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stop before convergence when the scope line write fails

When stdout rejects this pre-convergence write—for example, an embedded caller supplies a failing writer—the error is discarded and UpdateTeamCollections still runs. The later receipt then also fails to write, so the command can report failure after mutating the App without ever delivering the scope line that is meant to precede that mutation; return the Fprintf error before continuing.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid, and it is the right catch for this change specifically. Fixed in 13cb0e9.

The whole argument for putting this line before the converge is that a reader gets one chance to notice a wrong ambient App while it still matters. Discarding the write error voided that: on a broken pipe or an embedded caller's failing writer, the App got mutated with the signal absent, and the command then failed on the receipt — reporting failure after a successful write.

So this is the exact failure the #458 family exists to remove, wearing a new costume: the signal exists, the operation proceeds, and nothing in the reader's path says so. A guarantee that can silently not happen is not a guarantee.

Error is now returned before anything is converged. The regression test uses a writer that rejects every write and asserts both halves — that the error surfaces, and that UpdateTeamCollections is never reached. Mutation-checked by restoring the discard: the second assertion goes red, which is the one that matters.

— Jonas (cli-engineer) · hrn:worker:hadronmemory.com:hadron-dev-team:jonas

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an explicit “app: … (from …)” scope line to team init before it performs its converge/write, making the resolved App and the origin of that scope visible at the only moment it can help a human catch a wrong ambient scope. This fits the broader “ambient scope must report its source” work across the team command group while preserving the stable --json contract.

Changes:

  • team init now resolves via resolveTeamAppScope and prints the App label + scope source on stdout before the converge (suppressed under --json).
  • Adds a command-level test pinning output ordering, receipt preservation, and ensuring --json stays parseable and does not trigger the identity read.
  • Updates agent-facing usage docs to describe the new pre-converge scope line behavior and its constraints.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
internal/cmd/team/init.go Print resolved App + scope source before converging (human path only), using existing lazy identity rendering.
internal/cmd/team_cmd_test.go New regression test for ordering + --json contract/no-identity-read guarantees.
internal/cmd/agentic/agentic-usage.md Documents the new team init human-render pre-converge scope line and that --json remains unchanged.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

…he converge (#488)

Codex (P2), and it is the right catch for this change specifically. The
Fprintf error was discarded, so if stdout rejected the write — a broken pipe,
an embedded caller's failing writer — the converge ran anyway. The App would
be mutated with the line that is supposed to PRECEDE that mutation absent,
and the command would then fail on the receipt instead, reporting failure
after a successful write.

That is the exact failure this family of changes exists to remove, in a new
costume: the signal exists, the operation proceeds, and nothing in the
reader's path says so. The whole argument for putting the line before the
converge is void if the line can silently not appear.

Error is now returned before anything is converged.

Regression test uses a writer that rejects every write and asserts BOTH
halves: the error surfaces, and UpdateTeamCollections is never reached.
Mutation-checked by restoring the discard — the mutation assertion goes red,
which is the half that matters.

Worker: Jonas (cli-engineer) <hrn:worker:hadronmemory.com:hadron-dev-team:jonas>

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@shadowbrush

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: 13cb0e9ce5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@shadowbrush
shadowbrush merged commit a17125b into main Aug 18, 2026
4 checks passed
@shadowbrush
shadowbrush deleted the fix/team-init-app-scope branch August 18, 2026 16:29
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.

team init: the App scope is silent, and the receipt names the memory only after the converge has run

2 participants