Skip to content

feat(team-setup): deterministic --directives wire + non-TTY fail-loud guard - #65

Merged
kanfil merged 7 commits into
mainfrom
feat/team-setup-headless-wire
Oct 31, 2026
Merged

kanfil merged 7 commits into
mainfrom
feat/team-setup-headless-wire

Conversation

@kanfil

@kanfil kanfil commented Oct 30, 2026

Copy link
Copy Markdown
Member

Closes the ADR-386-amendment Headless Team-Setup Constraint follow-up: adlc-cli team setup --directives <path|https-url> wires without spawning an LLM session, and non-TTY (or --format) without --directives fails loudly instead of hanging ~300s.

Phase 1 of ADR-474 (accepted). Phase 2 flags live on adlc-team-skills/feat/team-setup-deterministic-flags; the interim JS check here is deleted in Phase 3.

Tests: 832/832 pass (incl. team-setup-wire + team-mcp-chain).

factory-deploy added 2 commits October 30, 2026 23:28
… guard

ADR-386-amendment follow-up: team setup --directives <path> validates structure (skill-owned team-helpers.sh --wire when the installed skill has it, interim 6-path JS check otherwise) and wires .adlc/init-options.json without spawning an agent session. Non-TTY runs without --directives fail loudly with the deterministic alternative instead of hanging a headless agent on the interactive skill's mode selection (~300s timeout). Workspace commands[] accepts team setup <source> --directives <path>; e2e fixture + NOTE updated.
…cepts https URLs

TTY with --format json can never reach the interactive handoff (machine contract), so it refuses like non-TTY. --directives <https-url> clones (git clone into ./team-ai-directives, reusing existing clones) then wires — file:// and ssh:// rejected per the skill's Mode 1 Input Validation contract.
factory-deploy added 3 commits October 31, 2026 00:08
Fake git on PATH intercepts the https clone by copying a seeded repo (no network); asserts clone lands in ./team-ai-directives and init-options.json wires the resolved path.
realpathSync the temp parent (/var vs /private/var); node payload in git-stub.js with POSIX sh wrapper + git.cmd twin (extensionless node file hit /bin/sh on POSIX).
macOS: makeProject() canonicalizes /var -> /private/var so resolved dest compares equal. Windows: node spawnSync resolves git.exe before git.cmd, so binary interception cannot work there; the URL test takes the local-path wire branch on win32 (regex + reject paths still covered).
@kanfil

kanfil commented Oct 31, 2026

Copy link
Copy Markdown
Member Author

Loop: factory-review --self-heal (iter 0)

Run factory-review-pr65-298324e acquiring review lease on #65 (head 298324e).

@kanfil kanfil left a comment

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.

factory-review verdict — #65 (iter 0, self-heal)

Reviewed revision: head 298324e4a59512ed1e9c31cb09fefe171bbceb1a, base db2389610a712c979ffe67486f6e5c99e40f0287, merge-base db2389610a712c979ffe67486f6e5c99e40f0287. Exact-head detached checkout. Three-persona Round 1 (Auditor/Adversary/Pragmatist) + Round-2 cross-exam; tags via synthesize-findings.sh.

CI on this head: 10/10 SUCCESS.

🔴 Important (5) — blocks merge, fix-gated

id tag finding location
F1 cross-validated --format never parsed by parseArgs → refusal guard dead; on a TTY team setup --format json falls through to interactive session, contradicting the PR body's "--format fails loudly" claim src/dispatch.mjs:274-350, src/commands/team.mjs:200
F2 consensus --format TTY test is vacuous — execFile children have no TTY, passes via !isTTY regardless; masks F1 tests/team-setup-wire.test.mjs:120-136
F3→Nit cross-validated Credentialed --directives URL (user:TOKEN@) reaches git clone argv, .git/config, and error log; bypasses the repo's own splitGitCredentials/askpass/scrub path src/commands/team.mjs:143-148
F5 cross-validated HTTPS URL silently ignored when ./team-ai-directives exists — no origin verification, re-run wires stale/wrong directives while the PR claims deterministic clone-then-wire src/commands/team.mjs:143-154
F6 consensus http:// accepted despite https-only comment and error text — credentialed plaintext clone, no warning, no test coverage src/commands/team.mjs:143

(credential-exposure row above is F4 — id corrected in tags.json: findings.json F4 = credential exposure, F3 = split-write.)

💬 Nits (9, advisory — non-blocking)

  • F3 (downgraded from Important by Round-2 challenge, both lanes concur): --cwd split-write is unreachable — no --cwd setter exists; latent dead code (src/commands/team.mjs:111-119)
  • F7: unknown valued flags silently dropped; value leaks into positional args (src/dispatch.mjs:344)
  • F8: init-options.json mutated before failure paths (src/commands/team.mjs:111)
  • F9: mock-agent guard asserts wrong file (mock-agent.log vs ADLC_MOCK_AGENT_FILE=mock.log) (tests/team-setup-wire.test.mjs:114)
  • Bare --directives with no value silently degrades (CLI) while workspace errors loudly — inconsistent
  • Workspace forwarder handles only --directives <path>, not --directives=<path>
  • Non-TTY guard runs after writeInitOptions + possible network install (fail-fast ordering)
  • Interim 6-path check is existence-only (file named rules passes)
  • Workspace help row omits team setup --directives forwarding

Verdict

🔴 not-merge-ready — 5 Important findings (spec-deviation ×2, credential exposure, silent-stale-wire, transport security). Core Phase 1 wiring works and tests pass, but the --format half of the headline claim is unimplemented (masked by a vacuous test) and the URL-handling path lacks the repo's own credential hardening.

Self-heal engaged: fix step next on the 5 synthesis-tagged actionable Important findings, then independent converge at the new head. Strictly non-approving — human code-owner approval remains mandatory.

…ne, stale-dest guard

F1: parseArgs captures --format/--format= into flags.format (export parseArgs)
F2: F1 regression test at parse layer; fixed mock filename; --format on URL path
F4: credentialed https URL via splitGitCredentials + GIT_ASKPASS, masked logs
F5: existing dest verified by remote.origin.url match, mismatch fails loudly
F6: enforce ^https:// (http/file/ssh rejected) + scheme tests
@kanfil

kanfil commented Oct 31, 2026

Copy link
Copy Markdown
Member Author

Fix step (iter 0) → artifact

Fixed: F1 (--format parsing), F2 (genuine format-branch test + mock-log filename), F4 (credentialed-clone hygiene via workspace askpass/scrub reuse), F5 (stale-dest origin verify), F6 (https-only enforcement). Full suite 835/835 pass in fix worktree incl. F1-revert sanity. Pushed 298324e..0678a8a76024181de9c270b630414717dc88a0be on feat/team-setup-headless-wire.

…y git stub)

tests 321/322 rely on a #!/bin/sh fake git on PATH; on Windows
spawnSync('git') resolves the real git.exe so the clone hits the
network (exit 128). Same precedent as provider-transport.test.mjs:1710.
https-only scheme rejection tests still run on all platforms.
@kanfil

kanfil commented Oct 31, 2026

Copy link
Copy Markdown
Member Author

Fix step (iter 1, regression on CI Windows) → artifact

Win32-only: tests 321/322 used the POSIX-only git stub → real-network clone on windows-latest. Applied the provider-transport.test.mjs:1710 precedent ({ skip: process.platform === "win32" } + comment). Fixes F4/F5/F6 themselves untouched; F6 scheme tests stay Windows-runnable (no clone). Linux suite: 835 pass. Pushed 0678a8a..dd4e9faabcf016334a344676d66d4a17a9188286.

@kanfil kanfil left a comment

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.

factory-review verdict — #65 (iter 1 · merge-ready)

Re-reviewed all REVIEW.md passes on new head dd4e9faabcf016334a344676d66d4a17a9188286 (fixed 298324e→dd4e9faabcf016334a344676d66d4a17a9188286). CI on this head: 10/10 pass.

Iter-0 Important set {F1, F2, F4, F5, F6} — all verified resolved at the new head by independent converge (synthesis gate re-run locally; each finding re-checked against code + functional checks, not fix-agent claims). No new Important or Nit findings introduced by the fix commits. F3 stays Nit (Round-2 downgrade stands). Residuals are documented non-blocking nits (uppercase scheme fail-loud, bare --format same-as-all-valued-flags, win32 POSIX-stub skips matching the #1710 precedent).

🟢 merge-ready — no open Important findings on dd4e9faabcf016334a344676d66d4a17a9188286. Strictly advisory: human code-owner approval and branch protection remain mandatory; this verdict approves nothing and merges nothing.

@kanfil

kanfil commented Oct 31, 2026

Copy link
Copy Markdown
Member Author

Converge (iter 1) → DONE

Verified: F1, F2, F4, F5, F6 resolved; new-issue scan clean; finding set shrunk to empty (progress). No spec-correction signals. Prior findings JSON/tags re-synthesized locally.

@kanfil

kanfil commented Oct 31, 2026

Copy link
Copy Markdown
Member Author

Loop complete: DONE. Verdict merge-ready bound to dd4e9fa (head-stable at posting). Human code-owner approval still required; review agent approves/merges nothing. Worktrees cleaned.

@kanfil kanfil added the validation Passed automated factory-review validation; awaiting human approval label Oct 31, 2026
@kanfil
kanfil merged commit 7d6d502 into main Oct 31, 2026
10 checks passed
kanfil added a commit that referenced this pull request Oct 31, 2026
release: v2.7.0 — team-setup headless wire (#65) + opencode session_end via session.deleted (#66)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

validation Passed automated factory-review validation; awaiting human approval

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant