feat: scene awareness (P1 + P4)#114
Merged
Merged
Conversation
Introduces a `scene` concept: four named environments (home / office / public / audit) that each carry default knobs and a baseline-expectation prior. Scope of this PR is Phase 1 (scene enum + CLI/env + presence-gate per scene + title-bar chip) and Phase 4 (session_meta JSONL header + analyze consumption + LLM prompt injection). Phases 2 (scenes.yaml persistence + auto-detect) and 3 (other knobs going scene-aware) are deferred to follow-ups. Why: the 2026-05-21 audit + the user's BLE event-modal screenshot showed that one set of defaults can't cover both a home network (sparse, anomalies matter) and a corp office (dense, churn is the baseline). And when the JSONL gets handed to an LLM, the same data tells different stories depending on the environment -- without scene context the LLM either misreads office churn as an incident or misses real home-network novelty. What changes: - New `scenes` capability (proposal + design + tasks + spec). - CLI: --scene SCENE / DITING_SCENE env var (cli > env > home default). Active scene drives `presence_gate_s` default (home=5s, office=15s, public=30s, audit=0s). Existing --ble-presence-gate D continues to win over the scene default. - TUI: title-bar chip ([home] / [家] etc.) renders the active scene next to the existing view + scan interval. - Event log: every JSONL session opens with a `session_meta` line carrying scene + scene_source + diting_version + ssid + gateway_ip + hostname. Idempotent emit; per-event lines unchanged. Both --log and `diting monitor` write it. - Analyze: reader populates Report.scenes + scene_sources; Markdown report header surfaces the scene; multi-session globs aggregate the scene mix. - --for-llm: prompt template prepends a [Scene context] paragraph with the scene's plain-language prior, backfilled with observed BSSID + BLE-identifier counts when present. Multi-scene bundles ask the LLM to compare across. No behavioural break: home is the default, home=5s presence gate matches the v1.5.0 baseline exactly. Pre-scene-aware JSONLs degrade gracefully -- the analyzer shows "Scene: unknown (pre-scene-aware capture)" and the LLM prompt falls back to general priors. Scope deferred to follow-ups (P2 + P3): - ~/.config/diting/scenes.yaml per-network persistence - auto-detect heuristic on first launch - other knobs going scene-aware (roam_alert, bonjour_categories, lan_inventory, event_throttle) - "new network detected -- classified as ..." banner Tests: +21 (scene module 16, event_log session_meta 5, CLI scene 10, analyze session_meta + LLM context 11). Snapshot regression green. Both openspec validates clean. 826 pytest passed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2 tasks
chenchaoyi
added a commit
that referenced
this pull request
May 22, 2026
Applies the spec deltas from add-scene-awareness (#114) into the canonical openspec/specs/ tree: - NEW openspec/specs/scenes/spec.md — full capability spec (Purpose + 3 requirements + 8 scenarios) - MODIFIED specs/cli/spec.md — adds --scene SCENE requirement - MODIFIED specs/event-log/spec.md — adds session_meta requirement - MODIFIED specs/analyze/spec.md — adds session_meta consumer + --for-llm scene context requirements - MODIFIED specs/bluetooth-scanning/spec.md — presence_gate_s default sourced from scene, --ble-presence-gate flag overrides - MODIFIED specs/tui-shell/spec.md — adds subtitle scene chip requirement (EN + ZH formats) Moves the change dir to openspec/changes/archive/2026-05-22-add-scene-awareness/. All artifacts done, all tasks complete, validate --specs --strict green (22/22 — scenes is the 22nd capability). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 tasks
chenchaoyi
added a commit
that referenced
this pull request
May 22, 2026
P1 (#114) shipped scene awareness but kept resolution opt-in: scenes fired only via --scene or DITING_SCENE, defaulting to home otherwise. User feedback in real use: "I'm clearly in an office (many same-name APs, enterprise auth), why does diting still show [家]?" That's the P1 contract working as designed, but not the experience the user wants. P2 closes the gap with two pieces that work together: 1. scenes.yaml per-network persistence -- same idea as aps.yaml. Optional file in cwd (DITING_SCENES_FILE overrides path), git-ignored, human-curated only. Maps SSID -> scene (with gateway_mac as fallback for SSID-collision cases like eduroam). 2. Auto-detect heuristic -- when no CLI / env / yaml decides, diting inspects the active Wi-Fi connection synchronously at startup and classifies. Rules: - security contains "Enterprise" (case-insensitive) -> office - visible_bssid_count >= 30 -> office - otherwise -> home public stays opt-in (captive-portal detection needs active probing we don't want to do). Resolution precedence is now 5-tier (was 3): 1. --scene CLI flag 2. DITING_SCENE env var 3. scenes.yaml (SSID or gateway_mac match) 4. classify_environment heuristic 5. default home scene_source field extends from {cli, env, default} to {cli, env, yaml, auto, default} -- session_meta records exactly which tier resolved. Analyzer and LLM bundle pick up the new sources automatically (scene_summary already handles arbitrary source strings). Startup banner explains the choice when source is yaml or auto; cli / env are silent (user knows what they asked for). One line to stderr (so `diting monitor > log.jsonl` shells stay clean). DITING_SCENE_QUIET=1 silences the banner. Behavioural change: a fresh diting launch on a corp Wi-Fi now shows [office] / [公司] without the user passing any flag. Same on home Wi-Fi -> [home] / [家]. Upgrade-safe because the heuristic falls to `home` whenever it can't classify, matching the v1.5.0 / P1 default exactly. Tests: +30 (10 heuristic, 12 scenes_config, 8 startup resolution). 856 pytest passed. Snapshot regression green. Both openspec validates clean. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
chenchaoyi
added a commit
that referenced
this pull request
May 22, 2026
Scene awareness. diting now carries an explicit notion of where
the user is right now, threaded through pollers + the JSONL
session header + the LLM prompt bundle.
Four named scenes (home / office / public / audit), each with:
- per-scene BLE presence_gate_s default (home=5s, office=15s,
public=30s, audit=0s)
- per-scene LLM prior injected into --for-llm prompt template
- chip in the TUI title bar showing the active scene
Resolution precedence (5 tiers, highest first):
1. --scene SCENE CLI flag
2. DITING_SCENE env var
3. scenes.yaml SSID / gateway_mac match (per-network pinning,
mirrors aps.yaml pattern, git-ignored, human-curated only)
4. classify_environment heuristic on the active connection
(WPA-Enterprise auth -> office; >= 30 BSSIDs visible ->
office; otherwise home; public stays opt-in because
captive-portal detection without active probing is
unreliable)
5. home default
scene_source field on session_meta records the tier that won:
{cli, env, yaml, auto, default}. session_meta is a new JSONL
event type written as line 1 of every diting session, carrying
scene + scene_source + diting_version + ssid + gateway_ip +
hostname (BSSID intentionally left out for PII reasons).
PRs bundled: #114 (P1 + P4 scene awareness), #115 (archive),
#116 (P2 auto-detect + scenes.yaml), #117 (archive).
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Introduces a scene concept — four named environments (
home/office/public/audit) that each carry default knobs and a baseline-expectation prior. Scope of this PR is Phase 1 (scene enum + CLI/env + presence-gate per scene + title-bar chip) and Phase 4 (session_metaJSONL header + analyze consumption + LLM prompt injection). Phases 2 (scenes.yamlpersistence + auto-detect) and 3 (other knobs going scene-aware) are deferred to follow-ups, per the user-confirmed plan.Why
The 2026-05-21 EN ↔ ZH TUI audit + the user's BLE events-modal screenshot showed that one set of defaults can't cover both a home network (sparse, anomalies matter) and a corp office (dense, churn IS the baseline). And when
diting analyze --for-llmhands the JSONL to ChatGPT / Claude, the LLM has no idea whether the dense BLE churn it sees is "anomalous" (home) or "expected baseline" (office) — the same data tells different stories under different priors.Scene catalog
ble_presence_gate_shome(default)officepublicauditResolution precedence:
--sceneCLI flag >DITING_SCENEenv var >homedefault.--ble-presence-gate Dcontinues to override the scene's gate.What's in this PR
openspec/changes/add-scene-awareness/— proposal / design / tasks + newscenescapability + 5 delta files (cli / event-log / analyze / bluetooth-scanning / tui-shell)src/diting/scene.py— module-level state (mirrorsi18n.pypattern),HOME/OFFICE/PUBLIC/AUDITconstants,get_scene/set_scene/resolve_scene/scene_defaultssrc/diting/cli.py—_extract_scene_arg,_resolve_ble_presence_gate(scene_default=...), scene threaded into_run_tuiand_run_monitor, help text updatesrc/diting/event_log.py—EventLogger.emit_session_meta(scene, scene_source, ssid, gateway_ip), idempotent, first-line-of-sessionsrc/diting/tui.py—DitingAppacceptsscene+scene_source; subtitle chip via_build_subtitle; callsemit_session_metaimmediately after constructing the loggersrc/diting/analyze.py— reader collectssession_metaintoReport.scenes/Report.scene_sources;scene_summary+scene_llm_context_paragraphhelpers;render_markdownadds**Scene:**header line;build_llm_promptprepends[Scene context]paragraphsrc/diting/i18n.py— ZH scene names (家/公司/公共/排查), chip format, error messages, help-text updatetests/test_scene.py(new) — 16 tests covering resolution, defaults, knob mappingtests/test_event_log.py— 5 newsession_metatests (header order, idempotency, null fields, disabled no-op, all-fields)tests/test_cli.py— 10 new tests (--sceneparse / env / invalid / missing; scene-aware gate resolution)tests/test_analyze.py— 11 new tests (session_meta consumption, multi-scene mix, source promotion, Markdown header, LLM prompt context paragraph)README.md+docs/zh/README.md— new## ScenessectionCHANGELOG.md+docs/zh/CHANGELOG.md—## [Unreleased]→### Added, four bullets, EN ↔ ZH paritytests/TESTING.md+docs/zh/TESTING.md— new### scenessection + extendedevent-logandanalyzesectionsState machine (per-session)
Scene only changes the
gatevalue used in the PENDING → PRESENT transition.Test plan
uv run pytest— 826 passed (805 → 826, +21 new)uv run python scripts/tui_snapshot.py --mode regression— green; ZH subtitle now renders视图:Wi-Fi · 扫描间隔 7s · [家]and EN rendersview: BLE · [home]openspec validate --specs --strict— 21/21 passedopenspec validate add-scene-awareness --strict— validditing --help+DITING_LANG=zh diting --helpboth render new--sceneentry--scene officeagainst a corp Wi-Fi, confirm the events panel has the chip + the JSONL has thesession_metaline +diting analyze --for-llminjects the office priorWhat this PR explicitly does NOT do
~/.config/diting/scenes.yamlper-network persistence (Phase 2)These were intentionally scoped out to keep the PR reviewable and to validate the four-scene catalog in real use before locking in more surface.
Generated with Claude Code