Document and verify independent output destinations
This commit is contained in:
@@ -799,6 +799,41 @@ func TestSubprocessStdoutAndSelectedReportDirectory(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSubprocessUsesIndependentTranscriptReportAndDebugDestinations(t *testing.T) {
|
||||||
|
f := newProcessFixture(t, "normal", "[grammar]")
|
||||||
|
transcriptDir := filepath.Join(f.dir, "transcripts")
|
||||||
|
reportDir := filepath.Join(f.dir, "reports")
|
||||||
|
debugDir := filepath.Join(f.dir, "debug")
|
||||||
|
for _, dir := range []string{transcriptDir, reportDir} {
|
||||||
|
if err := os.Mkdir(dir, 0700); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.output = filepath.Join(transcriptDir, "corrected.json")
|
||||||
|
code, stdout, stderr := f.run(t, true, "--report-dir", reportDir, "--debug", "--work-dir", debugDir)
|
||||||
|
if code != 0 || stdout != "" || !strings.HasPrefix(stderr, "Completed:") {
|
||||||
|
t.Fatalf("code=%d stdout=%q stderr=%q", code, stdout, stderr)
|
||||||
|
}
|
||||||
|
if output, err := os.ReadFile(f.output); err != nil || !bytes.Contains(output, []byte("Hello world")) {
|
||||||
|
t.Fatalf("transcript: %s %v", output, err)
|
||||||
|
}
|
||||||
|
for _, name := range []string{"errors.yml", "recoveries.yml", "report.md"} {
|
||||||
|
if _, err := os.Stat(filepath.Join(reportDir, name)); err != nil {
|
||||||
|
t.Fatalf("report %s: %v", name, err)
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(filepath.Join(transcriptDir, name)); !os.IsNotExist(err) {
|
||||||
|
t.Fatalf("report %s appeared beside transcript: %v", name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dirs, err := filepath.Glob(filepath.Join(debugDir, "run-*"))
|
||||||
|
if err != nil || len(dirs) != 1 {
|
||||||
|
t.Fatalf("debug runs: %v %v", dirs, err)
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(filepath.Join(dirs[0], "correction-ledger.json")); err != nil {
|
||||||
|
t.Fatalf("debug capture: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSubprocessReportingAndDebugSelections(t *testing.T) {
|
func TestSubprocessReportingAndDebugSelections(t *testing.T) {
|
||||||
var expectedOutput, expectedSummary string
|
var expectedOutput, expectedSummary string
|
||||||
for _, selection := range []struct{ reporting, debug bool }{
|
for _, selection := range []struct{ reporting, debug bool }{
|
||||||
@@ -1016,6 +1051,44 @@ func TestSubprocessInvalidInputLeavesFailedReports(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSubprocessInvalidInputWritesFailedReportsToSelectedDirectory(t *testing.T) {
|
||||||
|
f := newProcessFixture(t, "normal", "[grammar]")
|
||||||
|
if err := os.WriteFile(f.transcript, []byte("not JSON"), 0600); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
reportDir := filepath.Join(f.dir, "failed-reports")
|
||||||
|
if err := os.Mkdir(reportDir, 0700); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
code, _, _ := f.run(t, true, "--report-dir", reportDir)
|
||||||
|
if code != 1 || f.provider.callCount() != 0 {
|
||||||
|
t.Fatalf("invalid input dispatched: code=%d calls=%d", code, f.provider.callCount())
|
||||||
|
}
|
||||||
|
raw, err := os.ReadFile(filepath.Join(reportDir, "errors.yml"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var document reporting.ErrorsDocument
|
||||||
|
if err := yaml.Unmarshal(raw, &document); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if document.Status != reporting.StatusFailed || len(document.Errors) != 1 || document.Errors[0].Scope != "run" {
|
||||||
|
t.Fatalf("failed report: %+v", document)
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(filepath.Join(reportDir, "recoveries.yml")); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if raw, err := os.ReadFile(filepath.Join(reportDir, "report.md")); err != nil || !bytes.Contains(raw, []byte("Status: failed")) {
|
||||||
|
t.Fatalf("Markdown status: %s %v", raw, err)
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(filepath.Join(f.dir, "errors.yml")); !os.IsNotExist(err) {
|
||||||
|
t.Fatalf("failed report appeared in default directory: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(f.output); !os.IsNotExist(err) {
|
||||||
|
t.Fatalf("failed transcript published: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSubprocessOutputFailureBeforeModelDispatch(t *testing.T) {
|
func TestSubprocessOutputFailureBeforeModelDispatch(t *testing.T) {
|
||||||
f := newProcessFixture(t, "normal", "[grammar]")
|
f := newProcessFixture(t, "normal", "[grammar]")
|
||||||
f.output = filepath.Join(f.dir, "missing", "output.json")
|
f.output = filepath.Join(f.dir, "missing", "output.json")
|
||||||
|
|||||||
17
docs/cli.md
17
docs/cli.md
@@ -48,12 +48,17 @@ validation. The shared setting flags are:
|
|||||||
An omitted or empty output value selects stdout. Nonempty output filenames are
|
An omitted or empty output value selects stdout. Nonempty output filenames are
|
||||||
used literally, including whitespace.
|
used literally, including whitespace.
|
||||||
Reporting defaults to enabled. `--reporting=false` disables all three ordinary
|
Reporting defaults to enabled. `--reporting=false` disables all three ordinary
|
||||||
reports; an explicit false overrides a true file or overlay setting. With file
|
reports; an explicit false overrides a true file or overlay setting. For stdout
|
||||||
output, reports go beside the transcript and `--report-dir` has no effect. With
|
output, reports use the effective `reporting.stdout_dir`, which defaults to the
|
||||||
stdout output, `--report-dir` selects the report directory, defaulting to the
|
invocation working directory. For file output, they go beside the transcript
|
||||||
invocation working directory. `--debug` separately enables retained detailed
|
unless `--report-dir` is explicitly supplied. The flag selects the report
|
||||||
artifacts under `--work-dir`. Configuration inspection does not create or probe
|
directory for either transcript destination; a config-file or overlay value for
|
||||||
these output directories.
|
`reporting.stdout_dir` alone affects stdout output only. A selected report
|
||||||
|
directory must be nonblank when reporting is enabled. With reporting disabled,
|
||||||
|
the report destination is inactive and is not probed. `--debug` separately
|
||||||
|
enables retained detailed artifacts under `--work-dir`; selecting `--work-dir`
|
||||||
|
alone does not enable capture. Configuration inspection does not create or
|
||||||
|
probe these output directories.
|
||||||
Per-role model, base URL, key, timeout, retry, and concurrency flags have been
|
Per-role model, base URL, key, timeout, retry, and concurrency flags have been
|
||||||
removed. Models and generation controls belong to PromptKit profiles, backend
|
removed. Models and generation controls belong to PromptKit profiles, backend
|
||||||
credentials and capacity to configuration, and retry/deadline settings to the
|
credentials and capacity to configuration, and retry/deadline settings to the
|
||||||
|
|||||||
@@ -62,17 +62,20 @@ field provenance separately from ConfigKit's redacted settings YAML.
|
|||||||
| `thresholds.glossary`, `.homophones`, `.spoken_word`, `.grammar` | `0.8` each | Confidence thresholds in `[0, 1]`. |
|
| `thresholds.glossary`, `.homophones`, `.spoken_word`, `.grammar` | `0.8` each | Confidence thresholds in `[0, 1]`. |
|
||||||
| `context.description` | Empty | Trimmed prompt context, at most 500 bytes. |
|
| `context.description` | Empty | Trimmed prompt context, at most 500 bytes. |
|
||||||
| `reporting.enabled` | `true` | Write the ordinary three-file report bundle when processing. |
|
| `reporting.enabled` | `true` | Write the ordinary three-file report bundle when processing. |
|
||||||
| `reporting.stdout_dir` | `.` | Report directory for stdout transcript output; ignored for file output or disabled reporting. |
|
| `reporting.stdout_dir` | `.` | Report directory for stdout transcript output; a `--report-dir` flag can select reports for either transcript destination. |
|
||||||
| `diagnostics.debug` | `false` | Persist detailed debug artifacts when processing. |
|
| `diagnostics.debug` | `false` | Persist detailed debug artifacts when processing. |
|
||||||
| `diagnostics.work_dir` | `/tmp/audita` | Parent of the retained debug run directory; required only when debug is enabled. |
|
| `diagnostics.work_dir` | `/tmp/audita` | Parent of the retained debug run directory; required only when debug is enabled. |
|
||||||
|
|
||||||
Module keys select correction modules; validator chains remain built into them.
|
Module keys select correction modules; validator chains remain built into them.
|
||||||
The loader preserves module order and repetitions. Normalization time settings
|
The loader preserves module order and repetitions. Normalization time settings
|
||||||
accept numeric seconds only. The operation timeout uses a duration string.
|
accept numeric seconds only. The operation timeout uses a duration string.
|
||||||
`reporting.stdout_dir` must be nonblank only for reporting-enabled stdout runs;
|
`reporting.stdout_dir` must be nonblank when it supplies the active report
|
||||||
`diagnostics.work_dir` must be nonblank only with debug enabled. These path
|
destination: for stdout output, or for file output when `--report-dir` is
|
||||||
requirements are checked when choosing process destinations, not during
|
explicitly supplied. File output without that flag uses the transcript's parent,
|
||||||
configuration inspection.
|
so the configured value is inactive. Reporting-disabled runs do not select a
|
||||||
|
report directory. `diagnostics.work_dir` must be nonblank only with debug
|
||||||
|
enabled. These path requirements are checked when choosing process destinations,
|
||||||
|
not during configuration inspection.
|
||||||
|
|
||||||
## Credentials
|
## Credentials
|
||||||
|
|
||||||
|
|||||||
@@ -9,11 +9,14 @@ The [CLI reference](../cli.md) owns command syntax, streams, and exit semantics;
|
|||||||
Use an argument array with the transcript path and required glossary argument,
|
Use an argument array with the transcript path and required glossary argument,
|
||||||
following the [process examples](../cli.md#commands-and-help). For machine workflows,
|
following the [process examples](../cli.md#commands-and-help). For machine workflows,
|
||||||
select an explicit transcript file destination and a configuration path. Reports
|
select an explicit transcript file destination and a configuration path. Reports
|
||||||
then go beside the transcript. For stdout output, select `--report-dir` or use
|
go beside the transcript unless an explicit `--report-dir` selects another
|
||||||
the invocation working directory. Debug is independent and off by default.
|
directory. For stdout output, reports use the effective `reporting.stdout_dir`
|
||||||
Supplying a configuration path also
|
(defaulting to the invocation working directory), including a `--report-dir`
|
||||||
makes file selection predictable; only explicitly referenced credential variables
|
override. A config-file or overlay value for `reporting.stdout_dir` alone does
|
||||||
are read from the environment.
|
not redirect file-output reports. `--work-dir` selects a debug root, while
|
||||||
|
configuration or `--debug` enables capture; the path flag alone does not.
|
||||||
|
Supplying a configuration path also makes file selection predictable; only
|
||||||
|
explicitly referenced credential variables are read from the environment.
|
||||||
|
|
||||||
Resolve the executable deliberately and set the child's working directory so
|
Resolve the executable deliberately and set the child's working directory so
|
||||||
relative input/output paths and reported artifact paths have a known base.
|
relative input/output paths and reported artifact paths have a known base.
|
||||||
@@ -28,7 +31,8 @@ Consume transcript/report data through the
|
|||||||
[output-file contract](output-files.md), and treat stderr as human-readable detail.
|
[output-file contract](output-files.md), and treat stderr as human-readable detail.
|
||||||
The three reports may be absent after early configuration or destination
|
The three reports may be absent after early configuration or destination
|
||||||
failures. After initialization, failed reports can describe invalid input or
|
failures. After initialization, failed reports can describe invalid input or
|
||||||
partial processing. Exit `0` can include recoverable model errors and applied
|
partial processing and use the same selected report directory as successful
|
||||||
|
runs. Exit `0` can include recoverable model errors and applied
|
||||||
corrections; inspect `errors.yml` and `recoveries.yml` for those outcomes. A
|
corrections; inspect `errors.yml` and `recoveries.yml` for those outcomes. A
|
||||||
transcript may have been written before a later required report failure, so
|
transcript may have been written before a later required report failure, so
|
||||||
the file alone does not prove completion.
|
the file alone does not prove completion.
|
||||||
|
|||||||
@@ -17,10 +17,14 @@ settings, and the [output contract](integrations/output-files.md) owns schemas.
|
|||||||
## Required Output Files
|
## Required Output Files
|
||||||
|
|
||||||
Reporting is enabled by default. For file transcript output, `errors.yml`,
|
Reporting is enabled by default. For file transcript output, `errors.yml`,
|
||||||
`recoveries.yml`, and `report.md` go in the transcript output's parent directory.
|
`recoveries.yml`, and `report.md` go in the transcript output's parent directory
|
||||||
For stdout transcript output, they go in `reporting.stdout_dir` (default `.`),
|
unless `--report-dir` is explicitly supplied. For stdout transcript output, they
|
||||||
overridden by `--report-dir`. `--reporting=false` suppresses all three reports
|
go in the effective `reporting.stdout_dir` (default `.`), including a
|
||||||
and removes their path requirements. It does not suppress the stderr summary or
|
`--report-dir` override. A config-file or overlay value for
|
||||||
|
`reporting.stdout_dir` alone does not redirect file-output reports. An active
|
||||||
|
selected report directory must be nonblank. `--reporting=false` suppresses all
|
||||||
|
three reports and leaves their destination inactive, so it is not probed merely
|
||||||
|
because `--report-dir` was supplied. It does not suppress the stderr summary or
|
||||||
fatal errors. Stdout contains only transcript JSON; file output leaves it empty.
|
fatal errors. Stdout contains only transcript JSON; file output leaves it empty.
|
||||||
|
|
||||||
Existing report directories and parents for regular transcript files must be
|
Existing report directories and parents for regular transcript files must be
|
||||||
@@ -32,8 +36,9 @@ Preparation never truncates an existing transcript. Device and pipe destinations
|
|||||||
(such as `/dev/null` and named pipes) receive ordinary writes at publication time;
|
(such as `/dev/null` and named pipes) receive ordinary writes at publication time;
|
||||||
they are not opened during preflight or replaced with regular files. A pipe write
|
they are not opened during preflight or replaced with regular files. A pipe write
|
||||||
can wait for its reader, and stream write errors are fatal. With reporting enabled,
|
can wait for its reader, and stream write errors are fatal. With reporting enabled,
|
||||||
reports still use the output path's parent; disable reporting when that placement
|
reports still use the output path's parent unless an explicit `--report-dir`
|
||||||
is unsuitable for a stream destination.
|
selects another directory; disable reporting when that placement is unsuitable
|
||||||
|
for a stream destination.
|
||||||
|
|
||||||
With reporting enabled, all three reports are initialized as `in_progress` before
|
With reporting enabled, all three reports are initialized as `in_progress` before
|
||||||
input processing. Output is encoded fully before publication. Each regular output
|
input processing. Output is encoded fully before publication. Each regular output
|
||||||
@@ -48,10 +53,12 @@ finalized where possible. An uncatchable termination can leave `in_progress`.
|
|||||||
|
|
||||||
## Diagnostics Directory
|
## Diagnostics Directory
|
||||||
|
|
||||||
`--debug` enables detailed evidence independently of reporting. One directory
|
`--debug` or `diagnostics.debug` enables detailed evidence independently of
|
||||||
named `run-<id>` is created under `diagnostics.work_dir` (default `/tmp/audita`)
|
reporting. One directory named `run-<id>` is created under
|
||||||
and retained on success or failure. Without debug, no directory or payload
|
`diagnostics.work_dir` (default `/tmp/audita`), which `--work-dir` can override,
|
||||||
files are created. The parent is created when needed only for debug. Failure to
|
and retained on success or failure. Selecting `--work-dir` alone does not enable
|
||||||
|
capture. Without debug, no directory or payload files are created. The parent
|
||||||
|
is created when needed only for debug. Failure to
|
||||||
write optional debug evidence prints a redacted stderr diagnostic but does not
|
write optional debug evidence prints a redacted stderr diagnostic but does not
|
||||||
change correction decisions or the exit status.
|
change correction decisions or the exit status.
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Implementation Plan: Independent Output Destinations
|
# Implementation Plan: Independent Output Destinations
|
||||||
|
|
||||||
**Last updated:** 2026-10-03
|
**Last updated:** 2026-10-03
|
||||||
**Status:** Stage 1 complete; Stage 2 pending.
|
**Status:** Complete; Stages 1–2 implemented and verified.
|
||||||
**Audience:** gpt-5.6-terra coding agent.
|
**Audience:** gpt-5.6-terra coding agent.
|
||||||
|
|
||||||
## Scope and Execution
|
## Scope and Execution
|
||||||
@@ -94,6 +94,9 @@ destination constraints, then commit the coherent code/test unit.
|
|||||||
**Files:** `cmd/audita/main_integration_test.go`, `docs/cli.md`,
|
**Files:** `cmd/audita/main_integration_test.go`, `docs/cli.md`,
|
||||||
`docs/config.md`, `docs/operations.md`,
|
`docs/config.md`, `docs/operations.md`,
|
||||||
`docs/integrations/subprocess.md`, and `docs/roadmap/narratio-feature.md`.
|
`docs/integrations/subprocess.md`, and `docs/roadmap/narratio-feature.md`.
|
||||||
|
|
||||||
|
**Completion:** Complete — the full Go test suite, maintained configuration
|
||||||
|
examples, affected Markdown links, and whitespace checks pass.
|
||||||
Touch another document only if its current statement actually conflicts with
|
Touch another document only if its current statement actually conflicts with
|
||||||
the new behavior.
|
the new behavior.
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
**Last updated:** 2026-10-03
|
**Last updated:** 2026-10-03
|
||||||
|
|
||||||
**Status:** Proposed; not implemented. This roadmap records the intended external
|
**Status:** Implemented and verified. This roadmap records the external behavior
|
||||||
behavior and the reasons for it. The [implementation plan](implementation.md)
|
and the reasons for it. The [implementation plan](implementation.md) records the
|
||||||
owns the ordered work; the [Narratio request](feature-request-narratio.md)
|
completed work; the [Narratio request](feature-request-narratio.md) provides the
|
||||||
provides the consumer context.
|
consumer context.
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
@@ -21,8 +21,9 @@ working-state lifecycle; Narratio owns its destination paths and operator views.
|
|||||||
- `--output FILE` writes the transcript to `FILE`, or stdout when omitted.
|
- `--output FILE` writes the transcript to `FILE`, or stdout when omitted.
|
||||||
- Ordinary reporting is enabled by default. With file output, `errors.yml`,
|
- Ordinary reporting is enabled by default. With file output, `errors.yml`,
|
||||||
`recoveries.yml`, and `report.md` go beside the transcript. With stdout output,
|
`recoveries.yml`, and `report.md` go beside the transcript. With stdout output,
|
||||||
`--report-dir DIR` or `reporting.stdout_dir` selects their directory. The
|
`--report-dir DIR` or `reporting.stdout_dir` selects their directory. An
|
||||||
current selection logic ignores `--report-dir` when `--output` is supplied.
|
explicit `--report-dir` also selects reports for file output; without it, the
|
||||||
|
configured `reporting.stdout_dir` value remains stdout-only.
|
||||||
- Reports are initialized before input processing, then updated on completion or
|
- Reports are initialized before input processing, then updated on completion or
|
||||||
failure where possible. Early configuration or destination failures can occur
|
failure where possible. Early configuration or destination failures can occur
|
||||||
before report initialization and therefore leave no report files.
|
before report initialization and therefore leave no report files.
|
||||||
@@ -30,10 +31,10 @@ working-state lifecycle; Narratio owns its destination paths and operator views.
|
|||||||
enable debug capture. Capture is enabled by `diagnostics.debug` or an explicit
|
enable debug capture. Capture is enabled by `diagnostics.debug` or an explicit
|
||||||
`--debug`; when enabled, Audita retains a `run-<id>` directory beneath the
|
`--debug`; when enabled, Audita retains a `run-<id>` directory beneath the
|
||||||
selected root. Debug remains optional, including after unsuccessful runs.
|
selected root. Debug remains optional, including after unsuccessful runs.
|
||||||
- The output session already receives distinct transcript and report paths and
|
- The output session receives distinct transcript and report paths and checks
|
||||||
checks active destinations for conflicts with each other and protected inputs.
|
active destinations for conflicts with each other and protected inputs. The
|
||||||
The limitation is primarily in CLI report-directory selection, not in report
|
CLI selects the report destination while the output session retains its
|
||||||
encoding or the output session's basic shape.
|
existing collision checks, preflight, initialization, and finalization.
|
||||||
|
|
||||||
The current contracts are in [CLI](../cli.md), [configuration](../config.md),
|
The current contracts are in [CLI](../cli.md), [configuration](../config.md),
|
||||||
[operations](../operations.md), [subprocess guidance](../integrations/subprocess.md),
|
[operations](../operations.md), [subprocess guidance](../integrations/subprocess.md),
|
||||||
|
|||||||
@@ -215,6 +215,17 @@ func TestReportingAndDebugOverridesRemainIndependent(t *testing.T) {
|
|||||||
t.Fatalf("%s did not honor explicit false: %+v", command, selected.Result.Config)
|
t.Fatalf("%s did not honor explicit false: %+v", command, selected.Result.Config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
processInput, _, err := parseConfigInput("process", []string{"input.json", "--glossary", "glossary.yml", "--config", main, "--overlay", "testing"}, io.Discard)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
processConfig, err := loadConfigInput(processInput, "process")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if got, err := selectedReportDir(processConfig.Result.Config, filepath.Join("out", "corrected.json"), processInput.ReportDirSet); err != nil || got != "out" {
|
||||||
|
t.Fatalf("overlay stdout directory redirected file output: %q %v", got, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReportDirectorySelectionOnlyUsesActiveDestination(t *testing.T) {
|
func TestReportDirectorySelectionOnlyUsesActiveDestination(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user