Skip to content

Chore/untrack internal notes#38

Merged
ysyneu merged 10 commits intomainfrom
chore/untrack-internal-notes
May 2, 2026
Merged

Chore/untrack internal notes#38
ysyneu merged 10 commits intomainfrom
chore/untrack-internal-notes

Conversation

@ysyneu
Copy link
Copy Markdown
Collaborator

@ysyneu ysyneu commented May 2, 2026

Summary

Brief description of what this PR does.

Related Issue

Closes #

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)

Checklist

  • My code follows the project's coding standards
  • I have run make fmt and make lint
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • I have updated the documentation accordingly
  • I have added entries to the changelog (if applicable)

ysyneu and others added 9 commits May 2, 2026 16:39
CurrentTime was captured once via collectEnvironmentInfo() at runner
start and the resulting EnvInfo was sent only on the first heartbeat
(envInfoSent gate), so a long-lived runner kept reporting its boot
time forever. Safari then served that stale value via the `now` tool.

Static info (OS, arch, hostname, timezone) is fine to send once;
current time is by definition not static. Drop the field entirely —
safari uses its own wall clock now.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cloud sandbox pool pods spend their entire pre-claim lifetime dialing
safari and getting 401 (no t_cloud_sandbox_0 row exists yet — claim
inserts it). Two timing knobs were tuned for long-lived BYOC outage
recovery and hurt cloud claim latency:

  - initialReconnectDelay 1s -> 200ms: first dials race the row INSERT.
    The 1s delay added 1-2s of extra 401-retry cost before hostname
    auth could possibly succeed.

  - maxReconnectDelay 5min -> 10s: a pool pod waiting >50s slid into a
    32-64s exponential-backoff sleep, and got declared unrecoverable by
    safari's 90s claim window before its next dial — observed live
    (sbx_bzQ... did not come online within 1m30s, traceid
    6996857e714cb99c31665d043ed3c260). 10s stays well inside the claim
    window and remains acceptable for BYOC server-restart recovery.

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

safePath defense-in-depth — runner cannot rely on safari to filter rel_paths
that mirror the workspace root. Joining e.root with such a path silently
created a nested duplicate workspace tree (matching nested copies were
observed on a long-running BYOC host). Reject them at the runner boundary.

Read now appends a bounded sibling-name list when stat returns ENOENT, so
the agent can self-correct a near-miss filename without spending a turn on
an extra list call.

.golangci.yml: exclude gosec G706 (log-taint via taint analysis). slog's
structured key-value logging is not a format-string injection vector; the
rule fires on every slog call with a user-controlled value, which is the
whole point of structured logging.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- SyncSkill now checks .checksum+SKILL.md before install (cache hit
  returns {Cached:true,Path}; miss with no zip returns {Cached:false}
  so cloud retries with zip_data)
- Skills land at <home>/skills/<name>/ instead of <home>/.work/skills/<name>/
- Default home moves from ~/.flashduty-runner/workspace to ~/.flashduty
- FLASHDUTY_RUNNER_HOME is the new canonical override; FLASHDUTY_RUNNER_WORKSPACE
  kept as deprecated alias
- Add SyncSkillResult.Cached field (omitempty); SyncSkillArgs unchanged

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- resolveDir(t, dir) replaces duplicated EvalSymlinks boilerplate in
  ProbeHit and InstallOverwrites tests
- errors.Is(err, os.ErrNotExist) replaces deprecated os.IsNotExist pattern

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ructured

Runner-side counterparts of fc-safari's 2026-05-02 builtin-tools refactor:

- environment/webfetch: SSRF guard via inlined safeHTTPTransport +
  safeCheckRedirect. Pre-flight validateURL refuses RFC1918 / loopback
  / link-local / IMDS before any TCP dial; CheckRedirect re-validates
  each hop so a 302 to 169.254.169.254 is refused. Inlined from
  go-pkg/x/netsafe (open-source repo cannot import internal modules).
- environment/htmlx: HTML-to-markdown / text helpers inlined from
  go-pkg/x/htmlx for the same reason. html-to-markdown is now a
  direct dep.
- environment/environment::unzipSkill: zip-slip closed — compares
  abs target against dest+separator and rejects names containing '..'.
- environment/environment::grepWithGo: was strings.Contains (literal)
  — now compiles regex, falls back with regex_compile_error surfaced.
  Default ignore (.git, node_modules, .flashduty) added; Scanner
  buffer raised to 1 MiB; rg exit-2 surfaces real I/O errors instead
  of silent "no matches"; pattern starting with '-' gets '--' prefix;
  rsplit on ':' so paths containing ':' parse cleanly.
- protocol.GrepArgs gains output_mode, context_before/after,
  head_limit, file_type, case_sensitive — forwarded to ripgrep flags.
- environment/environment::Bash: per-stream large-output (stderr now
  truncates to its own bash_stderr_*.txt); LimitedWriter honors the
  io.Writer contract (returns ErrOutputCapped at the cap, exposes
  Hit(), appends "[output capped at 10MB]" marker once).
- protocol.BashResult carries truncated_stdout/stderr,
  stdout_file_path/stderr_file_path, *_total_size; legacy fields
  remain populated so old safari clients keep working.
- environment/large_output::ShouldSkipForOutputsDir: substring match
  → word-boundary regex (no more 'thread '/'head ' false positives).
  WriteRaw lowercased to writeRaw (single internal caller).
- permission/permission: AST walk now visits CmdSubst / ProcSubst /
  nested Stmts so cat <(curl evil) and echo $(curl evil) hit the
  same rules. Redirect targets are checked instead of discarded
  (cmd > /etc/passwd refused). Canonical-form normalization on both
  rule and command sides (kubectl  get  pods matches kubectl get *);
  env-prefix dual evaluation (KUBECONFIG=x kubectl get pods still
  matches kubectl get *). Rules sort by literal-prefix specificity,
  first match wins. SafeReadOnlyRules drops echo */find * — find
  -delete / find -exec rm now denied by default; replaced with
  scoped allow patterns.

E2E verified live: bash returns the new structured shape, web
(action=fetch) reaches example.com via the safe transport, all 17+
new permission/permission_test scenarios pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- netsafe: guard http.DefaultTransport type-assertion (errcheck)
- environment: switch if-else chain to switch (gocritic), drop dead err=nil (ineffassign), rename shadowed err vars (govet)
- permission: syntax.ClbOut → syntax.RdrClob (staticcheck SA1019)
- cmd: add /health listener for Tencent AGS readiness probe; bind via ListenConfig.Listen with parent context (noctx) and document the all-interfaces bind (gosec G102)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ysyneu ysyneu force-pushed the chore/untrack-internal-notes branch from d04c4ac to 0772182 Compare May 2, 2026 08:46
…n windows

- .golangci.yml: pinned CI golangci-lint v2.4 rejects G706 under
  gosec.excludes (rule unknown to its gosec). Move suppression to the
  version-safe linters.exclusions.rules text match so both v2.4 and newer
  versions stay quiet. Same regression previously fixed in a60df85.
- environment/unzipSkill: filepath.IsAbs("/etc/passwd") returns false on
  Windows (no drive letter), so the absolute-path guard let the entry
  slip through and TestUnzipSkill_RejectsZipSlip/absolute-path-unix
  failed on windows-latest. Reject leading "/" or "\\" on the raw name
  before Clean normalizes separators.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ysyneu ysyneu merged commit ffb868a into main May 2, 2026
10 checks passed
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